Peristiwa Perangkat (API Audio Inti)

Peristiwa perangkat memberi tahu klien tentang perubahan status perangkat titik akhir audio dalam sistem. Berikut ini adalah contoh peristiwa perangkat:

  • Pengguna mengaktifkan atau menonaktifkan perangkat titik akhir audio dari Device Manager atau dari panel kontrol multimedia Windows, Mmsys.cpl.
  • Pengguna menambahkan adaptor audio ke sistem atau menghapus adaptor audio dari sistem.
  • Pengguna menyambungkan perangkat titik akhir audio ke dalam jack audio dengan deteksi jack-presence, atau menghapus perangkat titik akhir audio dari jack tersebut.
  • Pengguna mengubah peran perangkat yang ditetapkan ke perangkat.
  • Nilai properti perangkat berubah.

Penambahan atau penghapusan adaptor audio menghasilkan peristiwa perangkat untuk semua perangkat titik akhir audio yang terhubung ke adaptor. Empat item pertama dalam daftar sebelumnya adalah contoh perubahan status perangkat. Untuk informasi selengkapnya tentang status perangkat perangkat titik akhir audio, lihat DEVICE_STATE_XXX Konstanta. Untuk informasi selengkapnya tentang deteksi jack-presence, lihat Perangkat Titik Akhir Audio.

Klien dapat mendaftar untuk diberi tahu ketika peristiwa perangkat terjadi. Sebagai respons terhadap pemberitahuan ini, klien dapat secara dinamis mengubah caranya menggunakan perangkat tertentu, atau memilih perangkat lain untuk digunakan untuk tujuan tertentu.

Misalnya, jika aplikasi memutar trek audio melalui sekumpulan speaker USB, dan pengguna memutuskan speaker dari konektor USB, aplikasi menerima pemberitahuan peristiwa perangkat. Sebagai respons terhadap peristiwa tersebut, jika aplikasi mendeteksi bahwa sekumpulan speaker desktop terhubung ke adaptor audio terintegrasi pada motherboard sistem, aplikasi dapat melanjutkan pemutaran trek audio melalui speaker desktop. Dalam contoh ini, transisi dari speaker USB ke speaker desktop terjadi secara otomatis, tanpa mengharuskan pengguna untuk campur tangan dengan mengalihkan aplikasi secara eksplisit.

Untuk mendaftar untuk menerima pemberitahuan perangkat, klien memanggil metode IMMDeviceEnumerator::RegisterEndpointNotificationCallback. Ketika klien tidak lagi memerlukan pemberitahuan, klien membatalkannya dengan memanggil metode IMMDeviceEnumerator::UnregisterEndpointNotificationCallback. Kedua metode mengambil parameter input, bernama pClient, yang merupakan penunjuk ke instans antarmuka IMMNotificationClient.

Antarmuka IMMNotificationClient diimplementasikan oleh klien. Antarmuka berisi beberapa metode, yang masing-masing berfungsi sebagai rutinitas panggilan balik untuk jenis peristiwa perangkat tertentu. Ketika peristiwa perangkat terjadi di perangkat titik akhir audio, modul MMDevice memanggil metode yang sesuai di antarmuka IMMNotificationClient dari setiap klien yang saat ini terdaftar untuk menerima pemberitahuan peristiwa perangkat. Panggilan ini meneruskan deskripsi peristiwa kepada klien. Untuk informasi selengkapnya, lihat Antarmuka IMMNotificationClient.

Klien yang terdaftar untuk menerima pemberitahuan peristiwa perangkat akan menerima pemberitahuan semua jenis peristiwa perangkat yang terjadi di semua perangkat titik akhir audio dalam sistem. Jika klien hanya tertarik pada jenis peristiwa tertentu atau di perangkat tertentu, maka metode dalam implementasi IMMNotificationClient harus memfilter peristiwa dengan tepat.

Windows SDK menyediakan sampel yang mencakup beberapa implementasi untuk Antarmuka IMMNotificationClient. Untuk informasi selengkapnya, lihat Sampel SDK yang Menggunakan API Audio Inti.

Contoh kode berikut menunjukkan kemungkinan implementasi antarmuka 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;
}

Kelas CMMNotificationClient dalam contoh kode sebelumnya adalah implementasi antarmuka IMMNotificationClient . Karena IMMNotificationClient mewarisi dari IUnknown, definisi kelas berisi implementasi metode IUnknown AddRef, Release, dan QueryInterface. Metode publik yang tersisa dalam definisi kelas khusus untuk antarmuka IMMNotificationClient . Metode ini adalah:

  • OnDefaultDeviceChanged, yang dipanggil ketika pengguna mengubah peran perangkat perangkat titik akhir audio.
  • OnDeviceAdded, yang dipanggil saat pengguna menambahkan perangkat titik akhir audio ke sistem.
  • OnDeviceRemoved, yang dipanggil ketika pengguna menghapus perangkat titik akhir audio dari sistem.
  • OnDeviceStateChanged, yang dipanggil saat status perangkat perangkat titik akhir audio berubah. (Untuk informasi selengkapnya tentang status perangkat, lihat DEVICE_STATE_ Konstanta XXX.)
  • OnPropertyValueChanged, yang dipanggil ketika nilai properti perangkat titik akhir audio berubah.

Masing-masing metode ini mengambil parameter input, pwstrDeviceId, yang merupakan penunjuk ke string ID titik akhir. String mengidentifikasi perangkat titik akhir audio tempat peristiwa perangkat terjadi.

Dalam contoh kode sebelumnya, _PrintDeviceName adalah metode privat di kelas CMMNotificationClient yang mencetak nama perangkat yang ramah. _PrintDeviceName mengambil string ID titik akhir sebagai parameter input. Ini meneruskan string ke metode IMMDeviceEnumerator::GetDevice. GetDevice membuat objek perangkat titik akhir untuk mewakili perangkat dan menyediakan antarmuka IMMDevice ke objek tersebut. Selanjutnya, _PrintDeviceName memanggil metode IMMDevice::OpenPropertyStore untuk mengambil antarmuka IPropertyStore ke penyimpanan properti perangkat. Terakhir, _PrintDeviceName memanggil metode IPropertyStore::GetItem untuk mendapatkan properti nama ramah perangkat. Untuk informasi selengkapnya tentang IPropertyStore, lihat dokumentasi Windows SDK.

Selain peristiwa perangkat, klien dapat mendaftar untuk menerima pemberitahuan peristiwa sesi audio dan peristiwa volume titik akhir. Untuk informasi selengkapnya, lihat Antarmuka IAudioSessionEvents dan Antarmuka IAudioEndpointVolumeCallback.

Perangkat Titik Akhir Audio