Partilhar via


Eventos de dispositivo (APIs de áudio principais)

Um evento de dispositivo notifica os clientes de uma alteração no status de um dispositivo de ponto de extremidade de áudio no sistema. Seguem-se 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 em uma tomada de áudio com deteção de presença de jack ou remove um dispositivo de ponto final 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 muda.

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 do dispositivo de dispositivos de ponto de extremidade de áudio, consulte DEVICE_STATE_XXX constantes. Para obter mais informações sobre deteção de presença de jack, consulte Audio Endpoint Devices.

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 através 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 detetar 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 de desktop 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 IMMDeviceEnumerator::RegisterEndpointNotificationCallback método. Quando o cliente não precisa mais de notificações, ele as cancela chamando o IMMDeviceEnumerator::UnregisterEndpointNotificationCallback método. Ambos os métodos usam um parâmetro de entrada, chamado pClient, que é um ponteiro para uma instância de interfaceIMMNotificationClient.

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 determinado tipo 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 de IMMNotificationClient do de cada cliente atualmente registrado para receber notificações de eventos de dispositivo. Essas chamadas passam uma descrição do evento para os clientes. Para obter mais informações, consulte IMMNotificationClient Interface.

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 evento ou em determinados dispositivos, os métodos em seu implementação de IMMNotificationClient devem filtrar os eventos adequadamente.

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

O exemplo de código a seguir mostra uma possível implementação do IMMNotificationClient interface:

//-----------------------------------------------------------
// 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 do IMMNotificationClient interface. Como IMMNotificationClient herda de IUnknown, a definição de classe contém implementações dos métodos IUnknownAddRef, Releasee 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 do 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 IMMDeviceEnumerator::GetDevice método. 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 IMMDevice::OpenPropertyStore método para recuperar o IPropertyStore interface para o repositório 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 final de áudio