Смена аудиоустройства

Тема в разделе "Библиотеки и интерфейсы", создана пользователем Jin X, 6 сен 2017.

  1. Jin X

    Jin X Active Member

    Публикаций:
    0
    Регистрация:
    15 янв 2009
    Сообщения:
    369
    Адрес:
    Кольца Сатурна
    У меня 2 вопроса:

    1. Как можно отследить (получить уведомление) изменение аудиоустройства по умолчанию или списка аудиоустройств (добавилось, отключилось)? Может, какое-то broadcast-сообщение посылается или ещё что-то?

    2. Во время записи при добавлении буфера в callback-функции функция waveInAddBuffer зависает, если было изменено аудиоустройство по умолчанию или удалено текущее устройство. Стандартная программа "Звукозапись" же, к примеру, спокойно переживает изменение аудиоустройства по умолчанию и даже переключает устройство на новое (т.е. продолжает запись с нового устройства).
    Как отслеживать подобные ситуации, чтобы прога не висла? Может, какую-то дополнительную проверку нужно делать через вызовом waveInAddBuffer?
     
  2. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    868
    Jin X нравится это.
  3. Jin X

    Jin X Active Member

    Публикаций:
    0
    Регистрация:
    15 янв 2009
    Сообщения:
    369
    Адрес:
    Кольца Сатурна
    1. Спасибо, а как это будет выглядеть на Asm (т.е. как использовать сие)? Там интерфейс же...
    И какие параметры подставлять.

    2. Короче, когда используется CALLBACK_WINDOW, то всё ок, а виснет при CALLBACK_FUNCTION.
    Ты какой использовал?
    У меня код на Delphi, большой довольно, см. аттач (это тестовый код для изучения разного рода штук с записью).
     

    Вложения:

    • WaveMeter.zip
      Размер файла:
      223,1 КБ
      Просмотров:
      536
    Последнее редактирование: 6 сен 2017
  4. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    868
    1. Создаешь COM объект который реализует IMMNotificationClient интерфейс и передаешь его в RegisterEndpointNotificationCallback. Потом система будет вызывать методы этого объекта в зависимости от событий. Учти что методы вызываются из разных потоков, также нужно исключить дедлоки, для этого нужно следовать правилам в описании интерфейса IMMNotificationClient.

    2. CALLBACK_WINDOW. Вообще в описании сказано что нельзя вызывать эту функцию в обработчике waveInProc иначе будут дедлок - как раз твой случай.
     
    Jin X нравится это.
  5. Jin X

    Jin X Active Member

    Публикаций:
    0
    Регистрация:
    15 янв 2009
    Сообщения:
    369
    Адрес:
    Кольца Сатурна
    1. Есть пример?

    Согласен :)
     
  6. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    868
    Завтра кину.
     
  7. Jin X

    Jin X Active Member

    Публикаций:
    0
    Регистрация:
    15 янв 2009
    Сообщения:
    369
    Адрес:
    Кольца Сатурна
  8. Thetrik

    Thetrik UA6527P

    Публикаций:
    0
    Регистрация:
    25 июл 2011
    Сообщения:
    868
    Код (C++):
    1. #include "stdafx.h"
    2. #include <CGuid.h>
    3. #include <InitGuid.h>
    4. #include <Mmdeviceapi.h>
    5. #include <Functiondiscoverykeys_devpkey.h>
    6. #include <iostream>
    7. #include <atlbase.h>
    8.  
    9. #define SAFE_RELEASE(punk)  \
    10.               if ((punk) != NULL)  \
    11.                 { (punk)->Release(); (punk) = NULL; }
    12.  
    13. class CMMNotificationClient : public IMMNotificationClient
    14. {
    15.     LONG _cRef;
    16.     IMMDeviceEnumerator *_pEnumerator;
    17.  
    18.     // Private function to print device-friendly name
    19.     HRESULT _PrintDeviceName(LPCWSTR  pwstrId);
    20.  
    21. public:
    22.     CMMNotificationClient() :
    23.         _cRef(1),
    24.         _pEnumerator(NULL)
    25.     {
    26.     }
    27.  
    28.     ~CMMNotificationClient()
    29.     {
    30.         SAFE_RELEASE(_pEnumerator)
    31.     }
    32.  
    33.     // IUnknown methods -- AddRef, Release, and QueryInterface
    34.  
    35.     ULONG STDMETHODCALLTYPE AddRef()
    36.     {
    37.         return InterlockedIncrement(&_cRef);
    38.     }
    39.  
    40.     ULONG STDMETHODCALLTYPE Release()
    41.     {
    42.         ULONG ulRef = InterlockedDecrement(&_cRef);
    43.         if (0 == ulRef)
    44.         {
    45.             delete this;
    46.         }
    47.         return ulRef;
    48.     }
    49.  
    50.     HRESULT STDMETHODCALLTYPE QueryInterface(
    51.                                 REFIID riid, VOID **ppvInterface)
    52.     {
    53.         if (IID_IUnknown == riid)
    54.         {
    55.             AddRef();
    56.             *ppvInterface = (IUnknown*)this;
    57.         }
    58.         else if (__uuidof(IMMNotificationClient) == riid)
    59.         {
    60.             AddRef();
    61.             *ppvInterface = (IMMNotificationClient*)this;
    62.         }
    63.         else
    64.         {
    65.             *ppvInterface = NULL;
    66.             return E_NOINTERFACE;
    67.         }
    68.         return S_OK;
    69.     }
    70.  
    71.     // Callback methods for device-event notifications.
    72.  
    73.     HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
    74.                                 EDataFlow flow, ERole role,
    75.                                 LPCWSTR pwstrDeviceId)
    76.     {
    77.         char  *pszFlow = "?????";
    78.         char  *pszRole = "?????";
    79.  
    80.         _PrintDeviceName(pwstrDeviceId);
    81.  
    82.         switch (flow)
    83.         {
    84.         case eRender:
    85.             pszFlow = "eRender";
    86.             break;
    87.         case eCapture:
    88.             pszFlow = "eCapture";
    89.             break;
    90.         }
    91.  
    92.         switch (role)
    93.         {
    94.         case eConsole:
    95.             pszRole = "eConsole";
    96.             break;
    97.         case eMultimedia:
    98.             pszRole = "eMultimedia";
    99.             break;
    100.         case eCommunications:
    101.             pszRole = "eCommunications";
    102.             break;
    103.         }
    104.  
    105.         printf("  -->New default device: flow = %s, role = %s\n",
    106.                pszFlow, pszRole);
    107.         return S_OK;
    108.     }
    109.  
    110.     HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
    111.     {
    112.         _PrintDeviceName(pwstrDeviceId);
    113.  
    114.         printf("  -->Added device\n");
    115.         return S_OK;
    116.     };
    117.  
    118.     HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
    119.     {
    120.         _PrintDeviceName(pwstrDeviceId);
    121.  
    122.         printf("  -->Removed device\n");
    123.         return S_OK;
    124.     }
    125.  
    126.     HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
    127.                                 LPCWSTR pwstrDeviceId,
    128.                                 DWORD dwNewState)
    129.     {
    130.         char  *pszState = "?????";
    131.  
    132.         _PrintDeviceName(pwstrDeviceId);
    133.  
    134.         switch (dwNewState)
    135.         {
    136.         case DEVICE_STATE_ACTIVE:
    137.             pszState = "ACTIVE";
    138.             break;
    139.         case DEVICE_STATE_DISABLED:
    140.             pszState = "DISABLED";
    141.             break;
    142.         case DEVICE_STATE_NOTPRESENT:
    143.             pszState = "NOTPRESENT";
    144.             break;
    145.         case DEVICE_STATE_UNPLUGGED:
    146.             pszState = "UNPLUGGED";
    147.             break;
    148.         }
    149.  
    150.         printf("  -->New device state is DEVICE_STATE_%s (0x%8.8x)\n",
    151.                pszState, dwNewState);
    152.  
    153.         return S_OK;
    154.     }
    155.  
    156.     HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
    157.                                 LPCWSTR pwstrDeviceId,
    158.                                 const PROPERTYKEY key)
    159.     {
    160.         _PrintDeviceName(pwstrDeviceId);
    161.  
    162.         printf("  -->Changed device property "
    163.                "{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}#%d\n",
    164.                key.fmtid.Data1, key.fmtid.Data2, key.fmtid.Data3,
    165.                key.fmtid.Data4[0], key.fmtid.Data4[1],
    166.                key.fmtid.Data4[2], key.fmtid.Data4[3],
    167.                key.fmtid.Data4[4], key.fmtid.Data4[5],
    168.                key.fmtid.Data4[6], key.fmtid.Data4[7],
    169.                key.pid);
    170.         return S_OK;
    171.     }
    172. };
    173.  
    174. // Given an endpoint ID string, print the friendly device name.
    175. HRESULT CMMNotificationClient::_PrintDeviceName(LPCWSTR pwstrId)
    176. {
    177.     HRESULT hr = S_OK;
    178.     IMMDevice *pDevice = NULL;
    179.     IPropertyStore *pProps = NULL;
    180.     PROPVARIANT varString;
    181.  
    182.     CoInitialize(NULL);
    183.     PropVariantInit(&varString);
    184.  
    185.     if (_pEnumerator == NULL)
    186.     {
    187.         // Get enumerator for audio endpoint devices.
    188.         hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
    189.                               NULL, CLSCTX_INPROC_SERVER,
    190.                               __uuidof(IMMDeviceEnumerator),
    191.                               (void**)&_pEnumerator);
    192.     }
    193.     if (hr == S_OK)
    194.     {
    195.         hr = _pEnumerator->GetDevice(pwstrId, &pDevice);
    196.     }
    197.     if (hr == S_OK)
    198.     {
    199.         hr = pDevice->OpenPropertyStore(STGM_READ, &pProps);
    200.     }
    201.     if (hr == S_OK)
    202.     {
    203.         // Get the endpoint device's friendly-name property.
    204.         hr = pProps->GetValue(PKEY_Device_FriendlyName, &varString);
    205.     }
    206.     printf("----------------------\nDevice name: \"%S\"\n"
    207.            "  Endpoint ID string: \"%S\"\n",
    208.            (hr == S_OK) ? varString.pwszVal : L"null device",
    209.            (pwstrId != NULL) ? pwstrId : L"null ID");
    210.  
    211.     PropVariantClear(&varString);
    212.  
    213.     SAFE_RELEASE(pProps)
    214.     SAFE_RELEASE(pDevice)
    215.     CoUninitialize();
    216.     return hr;
    217. }
    218.  
    219. int main()
    220. {
    221.     CoInitialize(NULL);
    222.  
    223.     setlocale(LC_ALL, "");
    224.  
    225.     {
    226.         CComPtr <IMMDeviceEnumerator>    pEnumerator;
    227.         CComPtr <IMMNotificationClient>    pClient;
    228.         HRESULT                            hr = S_OK;
    229.         CMMNotificationClient            cClient;
    230.  
    231.         if (FAILED(hr = cClient.QueryInterface(__uuidof(IMMNotificationClient), (void**)&pClient))) {
    232.             printf("QueryInterface failed %x", hr);
    233.             goto Exit;
    234.         }
    235.  
    236.         if (FAILED(hr = pEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL))) {
    237.             printf("CoCreateInstance failed %x", hr);
    238.             goto Exit;
    239.         }
    240.  
    241.         if (FAILED(hr = pEnumerator->RegisterEndpointNotificationCallback(pClient.p))) {
    242.             printf("RegisterEndpointNotificationCallback failed %x", hr);
    243.             goto Exit;
    244.         }
    245.  
    246.         getchar();
    247.  
    248.         if (FAILED(hr = pEnumerator->UnregisterEndpointNotificationCallback(pClient.p))) {
    249.             printf("UnregisterEndpointNotificationCallback failed %x", hr);
    250.             goto Exit;
    251.         }
    252.     }
    253.  
    254. Exit:
    255.  
    256.     CoUninitialize();
    257.  
    258.     return 0;
    259. }
    260.  
    261.  
     
    Последнее редактирование: 7 сен 2017
    Jin X нравится это.