Udostępnij za pośrednictwem


Zdarzenia urządzenia (podstawowe interfejsy API audio)

Zdarzenie urządzenia powiadamia klientów o zmianie stanu urządzenia urządzenia punktu końcowego audio w systemie. Poniżej przedstawiono przykłady zdarzeń urządzenia:

  • Użytkownik włącza lub wyłącza urządzenie punktu końcowego audio z Menedżera urządzeń lub z panelu sterowania multimediami systemu Windows, Mmsys.cpl.
  • Użytkownik dodaje adapter audio do systemu lub usuwa adapter audio z systemu.
  • Użytkownik podłącza urządzenie punktu końcowego audio do gniazda audio z wykrywaniem obecności gniazda lub usuwa urządzenie punktu końcowego audio z takiego gniazda.
  • Użytkownik zmienia rolę urządzenia przypisaną do urządzenia.
  • Wartość właściwości urządzenia zmienia się.

Dodanie lub usunięcie karty audio generuje zdarzenia urządzenia dla wszystkich urządzeń punktu końcowego audio, które łączą się z kartą. Pierwsze cztery elementy na powyższej liście to przykłady zmian stanu urządzenia. Aby uzyskać więcej informacji na temat stanów urządzeń punktu końcowego audio, zobacz DEVICE_STATE_XXX Stałe. Aby uzyskać więcej informacji na temat wykrywania obecności jacka, zobacz Audio Endpoint Devices.

Klient może zarejestrować się, aby otrzymywać powiadomienia o wystąpieniu zdarzeń urządzenia. W odpowiedzi na te powiadomienia klient może dynamicznie zmieniać sposób korzystania z określonego urządzenia lub wybrać inne urządzenie do użycia w określonym celu.

Jeśli na przykład aplikacja odtwarza ścieżkę dźwiękową przez zestaw głośników USB, a użytkownik odłącza głośniki od łącznika USB, aplikacja otrzymuje powiadomienie o zdarzeniu urządzenia. W odpowiedzi na zdarzenie, jeśli aplikacja wykryje, że zestaw głośników stacjonarnych jest podłączony do zintegrowanej karty audio na płycie głównej systemu, aplikacja może wznowić odtwarzanie ścieżki audio za pośrednictwem głośników stacjonarnych. W tym przykładzie przejście z głośników USB do głośników klasycznych odbywa się automatycznie, bez konieczności interwencji użytkownika przez jawne przekierowanie aplikacji.

Aby zarejestrować się w celu odbierania powiadomień urządzenia, klient wywołuje metodę IMMDeviceEnumerator::RegisterEndpointNotificationCallback. Gdy klient nie wymaga już powiadomień, anuluje je, wywołując metodę IMMDeviceEnumerator::UnregisterEndpointNotificationCallback. Obie metody przyjmują parametr wejściowy o nazwie pClient, który jest wskaźnikiem do wystąpienia interfejsu IMMNotificationClient.

Interfejs IMMNotificationClient jest implementowany przez klienta. Interfejs zawiera kilka metod, z których każda służy jako procedura wywołania zwrotnego dla określonego typu zdarzenia urządzenia. Gdy zdarzenie urządzenia występuje na urządzeniu punktu końcowego audio, moduł MMDevice wywołuje odpowiednią metodę w IMMNotificationClient interfejsu każdego klienta, który jest obecnie zarejestrowany w celu odbierania powiadomień o zdarzeniach urządzenia. Te wywołania przekazują opis zdarzenia do klientów. Aby uzyskać więcej informacji, zobacz interfejsU IMMNotificationClient.

Klient zarejestrowany do odbierania powiadomień o zdarzeniach urządzenia będzie otrzymywać powiadomienia o wszystkich typach zdarzeń urządzeń występujących we wszystkich urządzeniach punktu końcowego audio w systemie. Jeśli klient jest zainteresowany tylko niektórymi typami zdarzeń lub określonymi urządzeniami, metody w jego implementacji IMMNotificationClient powinny odpowiednio filtrować zdarzenia.

Zestaw Windows SDK zawiera przykłady obejmujące kilka implementacji interfejsu IMMNotificationClient. Aby uzyskać więcej informacji, zobacz przykłady zestawu SDK korzystające z podstawowych interfejsów API audio.

Poniższy przykład kodu przedstawia możliwą implementację interfejsu 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;
}

Klasa CMMNotificationClient w poprzednim przykładzie kodu jest implementacją interfejsu IMMNotificationClient. Ponieważ IMMNotificationClient dziedziczy z IUnknown, definicja klasy zawiera implementacje metod IUnknownAddRef, Releasei QueryInterface. Pozostałe metody publiczne w definicji klasy są specyficzne dla interfejsu IMMNotificationClient. Oto następujące metody:

  • OnDefaultDeviceChanged, który jest wywoływany, gdy użytkownik zmienia rolę urządzenia urządzenia punktu końcowego audio.
  • OnDeviceAdded, który jest wywoływany, gdy użytkownik dodaje urządzenie punktu końcowego audio do systemu.
  • OnDeviceRemoved, który jest wywoływany, gdy użytkownik usuwa urządzenie punktu końcowego audio z systemu.
  • OnDeviceStateChanged, który jest wywoływany, gdy zmieni się stan urządzenia punktu końcowego audio. (Aby uzyskać więcej informacji na temat stanów urządzeń, zobacz DEVICE_STATE_ XXX Stałe.)
  • OnPropertyValueChanged, który jest wywoływany, gdy wartość właściwości urządzenia punktu końcowego audio ulegnie zmianie.

Każda z tych metod przyjmuje parametr wejściowy, pwstrDeviceId, który jest wskaźnikiem do ciągu identyfikatora punktu końcowego. Ciąg identyfikuje urządzenie punktu końcowego audio, w którym wystąpiło zdarzenie urządzenia.

W poprzednim przykładzie kodu _PrintDeviceName jest prywatną metodą w klasie CMMNotificationClient, która wyświetla przyjazną nazwę urządzenia. _PrintDeviceName przyjmuje ciąg identyfikatora punktu końcowego jako parametr wejściowy. Przekazuje on ciąg do metody IMMDeviceEnumerator::GetDevice. GetDevice tworzy obiekt urządzenia punktu końcowego do reprezentowania urządzenia i udostępnia interfejs IMMDevice dla tego obiektu. Następnie _PrintDeviceName wywołuje metodę IMMDevice::OpenPropertyStore, aby pobrać interfejs IPropertyStore do magazynu właściwości urządzenia. Na koniec _PrintDeviceName wywołuje metodę IPropertyStore::GetItem, aby uzyskać przyjazną nazwę urządzenia. Aby uzyskać więcej informacji na temat IPropertyStore, zobacz dokumentację zestawu Windows SDK.

Oprócz zdarzeń urządzenia klienci mogą rejestrować się w celu odbierania powiadomień o zdarzeniach sesji audio i zdarzeniach woluminu punktu końcowego. Aby uzyskać więcej informacji, zobacz interfejs IAudioSessionEvents Interface i interfejsU IAudioEndpointVolumeCallback.

urządzenia punktu końcowego audio