Compartilhar via


Eventos de dispositivo (APIs de áudio principais)

Um evento de dispositivo notifica os clientes sobre uma alteração no status de um dispositivo de ponto de extremidade de áudio no sistema. A seguir estão exemplos de eventos de dispositivo:

  • O usuário habilita ou desabilita um dispositivo de ponto de extremidade de áudio do Gerenciador de dispositivos ou do painel de controle multimídia do Windows, Mmsys.cpl.
  • O usuário adiciona um adaptador de áudio ao sistema ou remove um adaptador de áudio do sistema.
  • O usuário conecta um dispositivo de ponto de extremidade de áudio a uma tomada de áudio com detecção de presença de tomada ou remove um dispositivo de ponto de extremidade de áudio de tal tomada.
  • O usuário altera a função de dispositivo atribuída a um dispositivo.
  • O valor de uma propriedade de um dispositivo é alterado.

A adição ou remoção de um adaptador de áudio gera eventos de dispositivo para todos os dispositivos de ponto de extremidade de áudio que se conectam ao adaptador. Os quatro primeiros itens da lista anterior são exemplos de alterações no estado do dispositivo. Para obter mais informações sobre os estados de dispositivo de dispositivos de ponto de extremidade de áudio, consulte Constantes de DEVICE_STATE_XXX. Para obter mais informações sobre a detecção de presença de jack, consulte Dispositivos de ponto de extremidade de áudio.

Um cliente pode se registrar para ser notificado quando ocorrerem eventos do dispositivo. Em resposta a essas notificações, o cliente pode alterar dinamicamente a maneira como usa um dispositivo específico ou selecionar um dispositivo diferente para usar para uma finalidade específica.

Por exemplo, se um aplicativo estiver reproduzindo uma faixa de áudio por meio de um conjunto de alto-falantes USB e o usuário desconectar os alto-falantes do conector USB, o aplicativo receberá uma notificação de evento do dispositivo. Em resposta ao evento, se o aplicativo detectar que um conjunto de alto-falantes da área de trabalho está conectado ao adaptador de áudio integrado na placa-mãe do sistema, o aplicativo poderá retomar a reprodução da faixa de áudio através dos alto-falantes da área de trabalho. Neste exemplo, a transição de alto-falantes USB para alto-falantes da área de trabalho ocorre automaticamente, sem exigir que o usuário intervenha redirecionando explicitamente o aplicativo.

Para se registrar para receber notificações de dispositivo, um cliente chama o método IMMDeviceEnumerator::RegisterEndpointNotificationCallback. Quando o cliente não requer mais notificações, ele as cancela chamando o método IMMDeviceEnumerator::UnregisterEndpointNotificationCallback. Ambos os métodos usam um parâmetro de entrada, chamado pClient, que é um ponteiro para uma instância de interface IMMNotificationClient.

A interface IMMNotificationClient é implementada por um cliente. A interface contém vários métodos, cada um dos quais serve como uma rotina de retorno de chamada para um tipo específico de evento de dispositivo. Quando um evento de dispositivo ocorre em um dispositivo de ponto de extremidade de áudio, o módulo MMDevice chama o método apropriado na interface IMMNotificationClient de cada cliente que está atualmente registrado para receber notificações de evento de dispositivo. Essas chamadas passam uma descrição do evento para os clientes. Para obter mais informações, consulte Interface IMMNotificationClient.

Um cliente registrado para receber notificações de eventos de dispositivo receberá notificações de todos os tipos de eventos de dispositivo que ocorrem em todos os dispositivos de ponto de extremidade de áudio no sistema. Se um cliente estiver interessado apenas em determinados tipos de eventos ou em determinados dispositivos, os métodos em sua implementação IMMNotificationClient devem filtrar os eventos apropriadamente.

O SDK do Windows fornece exemplos que incluem várias implementações para a interface IMMNotificationClient. Para obter mais informações, consulte Exemplos de SDK que usam as APIs de áudio principais.

O exemplo de código a seguir mostra uma possível implementação da 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;
}

A classe CMMNotificationClient no exemplo de código anterior é uma implementação da interface IMMNotificationClient . Como IMMNotificationClient herda de IUnknown, a definição de classe contém implementações dos métodos IUnknown AddRef, Release e QueryInterface. Os métodos públicos restantes na definição de classe são específicos para a interface IMMNotificationClient . Esses métodos são:

  • OnDefaultDeviceChanged, que é chamado quando o usuário altera a função de dispositivo de um dispositivo de ponto de extremidade de áudio.
  • OnDeviceAdded, que é chamado quando o usuário adiciona um dispositivo de ponto de extremidade de áudio ao sistema.
  • OnDeviceRemoved, que é chamado quando o usuário remove um dispositivo de ponto de extremidade de áudio do sistema.
  • OnDeviceStateChanged, que é chamado quando o estado do dispositivo de um dispositivo de ponto de extremidade de áudio é alterado. (Para obter mais informações sobre os estados do dispositivo, consulte DEVICE_STATE_ Constantes XXX.)
  • OnPropertyValueChanged, que é chamado quando o valor de uma propriedade de um dispositivo de ponto de extremidade de áudio é alterado.

Cada um desses métodos usa um parâmetro de entrada, pwstrDeviceId, que é um ponteiro para uma cadeia de caracteres de ID de ponto de extremidade. A cadeia de caracteres identifica o dispositivo de ponto de extremidade de áudio no qual o evento de dispositivo ocorreu.

No exemplo de código anterior, _PrintDeviceName é um método privado na classe CMMNotificationClient que imprime o nome amigável do dispositivo. _PrintDeviceName usa a cadeia de caracteres de ID do ponto de extremidade como um parâmetro de entrada. Ele passa a cadeia de caracteres para o método IMMDeviceEnumerator::GetDevice . GetDevice cria um objeto de dispositivo de ponto de extremidade para representar o dispositivo e fornece a interface IMMDevice para esse objeto. Em seguida, _PrintDeviceName chama o método IMMDevice::OpenPropertyStore para recuperar a interface IPropertyStore para o armazenamento de propriedades do dispositivo. Finalmente, _PrintDeviceName chama o método IPropertyStore::GetItem para obter a propriedade friendly-name do dispositivo. Para obter mais informações sobre IPropertyStore, consulte a documentação do SDK do Windows.

Além dos eventos do dispositivo, os clientes podem se registrar para receber notificações de eventos de sessão de áudio e eventos de volume de ponto de extremidade. Para obter mais informações, consulte IAudioSessionEvents Interface e IAudioEndpointVolumeCallback Interface.

Dispositivos de ponto de extremidade de áudio