2016-07-11 20 views
1

Я использую wasapi для захвата петли. Все данные после этого сохраняются в файле wav. Когда он начинает захватывать, он доставляет данные все время, пока я не прекращу процесс захвата, даже если приложение не использует аудиоустройство, и поэтому никто не производит музыку. В результате я пишу в файл не ценные данные - просто молчание.wasapi: обнаружение, что ничего не воспроизводится (устройство не используется)

Итак, есть ли способ отличной тишины в текущей музыке и ситуации, когда устройство вообще не используется. В более поздней ситуации я хочу прерывать процесс записи данных в файл и создавать новый, когда smth снова воспроизводятся через аудиоустройство. Метод

PS AudioCaptureClinet GetBuffer показан с выходными параметром flag, который, казалось бы, может иметь значение AUDCLNT_BUFFERFLAGS_SILENT == 0x1 в некоторых условиях, но в моем случае она возвращает flag==0 каждый

+0

Не можете ли вы просто проверить тишину? Например. остановить поток после X секунд записи нулей (или что-нибудь ниже некоторого порога?) –

+0

Конечно, это возможно. Но не изящный и не может быть надежным методом. Иногда в некоторых композициях (особенно в современном композиторе) у нас довольно длинные паузы (см. John Cage 4'33 ").))) – DotNetter

ответ

1

Хорошо, оказывается, нет никакого надежного способа вывести из потока петлевого что ничего не воспроизводится на соответствующем устройстве вывода. Тем не менее, можно запросить сеансы аудио на самом устройстве вывода и для каждого сеанса, проверить, что это состояние активно. Также можно получать уведомления, когда сеансы добавляются/удаляются с устройства и получать уведомление при активации сеанса (de). Таким образом, вы можете запустить поток loopback, когда первый сеанс активируется на устройстве и останавливает его, когда последний отключается.

Вот как это сделать:

  • Реализовать IAudioSessionNotification и IAudioSessionEvents
  • Настройка АПС нити, STA не будет работать
  • Получить IMMDevice интерфейс вывода вы хотите контролировать
  • Из IMMDevice получите IAudioSessionManager2
  • От IAudioSessionManager2, получите IAudioSessionEnumerator
  • От IAudioSessionEnumerator, перечисление IAudioSessionControls
    • Это список начальных сеансов. Он будет оставаться постоянным в течение срока службы счетчика.
    • От IAudioSessionControl используйте GetState, чтобы увидеть, что первоначально активный
    • Использование IAudioSessionControl :: RegisterAudioSessionNotification, чтобы получить уведомление о (де) активации начальных сессий
  • Использование IAudioSessionManager2 :: RegisterSessionNotification зарегистрировать свое пользовательское уведомление сеанса
    • Это будет вызвано для любых дополнительных сеансов, созданных после первоначального перечисления
    • От IAudioSessionNotification :: OnSessionCreated, используйте IAudioSessionControl :: RegisterAudioSessionNotification, чтобы получить уведомление о (де) активации дополнительных сессий
  • Если какой-либо сеанс изначально активен, запустить поток обратной петли
  • Когда последний (начальный или дополнительный) сеанс получает deactivaed или отключен, остановка циклический поток
  • И рестарта конечно, когда любой сеанс снова активирует

Ниже приведен рабочий пример. Обратите внимание, что я не:

  • ли какой-либо тщательной обработки ошибок, кроме печати, что случилось
  • На самом деле создан поток обратной петли. Пример только печатает, когда что-либо (де) активируется.
  • Выполняйте любую учетную запись COM-интерфейсов! Образец - это утечка интерфейсов. Чтобы правильно использовать этот материал, вы, вероятно, захотите сохранить объекты SessionNotifications/SessionEvents в некоторой поточно-безопасной коллекции и отменить регистрацию/выпуск, поскольку сеансы будут уничтожены.


    #include <windows.h> 
    #include <atlbase.h> 
    #include <atlcomcli.h> 
    #include <mmdeviceapi.h> 
    #include <audioclient.h> 
    #include <audiopolicy.h> 
    #include <iostream> 
    #include <vector> 
    #include <string> 

    #define VERIFY(expr) do {         \ 
     if(FAILED(hr = (expr))) {        \ 
     std::cout << "Error: " << #expr << ": " << hr << "\n"; \ 
     goto error;           \ 
     }              \ 
    } while(0) 

    static volatile ULONG sessionId = 0; 

    class SessionEvents : public IAudioSessionEvents { 

    private: 
     LONG rc; 
     ULONG id; 
     ~SessionEvents() {} 

    public: 
     SessionEvents(ULONG id) : 
     id(id), rc(1) {} 

     ULONG STDMETHODCALLTYPE AddRef() { 
     return InterlockedIncrement(&rc); 
     } 

     ULONG STDMETHODCALLTYPE Release() { 
     ULONG rc = InterlockedDecrement(&this->rc); 
     if(rc == 0) { 
      delete this; 
     } 
     return rc; 
     } 

     HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) { 
     if(IID_IUnknown == riid) { 
      AddRef(); 
      *ppv = static_cast<IUnknown*>(this); 
      return S_OK; 
     } 
     else if(__uuidof(IAudioSessionEvents) == riid) { 
      AddRef(); 
      *ppv = static_cast<IAudioSessionEvents*>(this); 
      return S_OK; 
     } 
     else { 
      *ppv = nullptr; 
      return E_NOINTERFACE; 
     } 
     } 

     HRESULT STDMETHODCALLTYPE OnDisplayNameChanged(LPCWSTR NewDisplayName, LPCGUID EventContext) { 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnIconPathChanged(LPCWSTR NewIconPath, LPCGUID EventContext) { 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnSimpleVolumeChanged(float NewVolume, BOOL NewMute, LPCGUID EventContext) { 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnChannelVolumeChanged(DWORD ChannelCount, float NewChannelVolumeArray[], DWORD ChangedChannel, LPCGUID EventContext) { 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnGroupingParamChanged(LPCGUID NewGroupingParam, LPCGUID EventContext) { 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnStateChanged(AudioSessionState NewState) { 
     switch(NewState) { 
     case AudioSessionStateInactive: std::wcout << L"AudioSessionStateInactive session # " << id << L"\n"; break; 
     case AudioSessionStateActive: std::wcout << L"AudioSessionStateActive session # " << id << L"\n"; break; 
     case AudioSessionStateExpired: std::wcout << L"AudioSessionStateExpired session # " << id << L"\n"; break; 
     } 
     return S_OK; 
     } 

     HRESULT STDMETHODCALLTYPE OnSessionDisconnected(AudioSessionDisconnectReason DisconnectReason) { 
     std::wcout << L"OnSessionDisconnected session # " << id << L"\n"; 
     return S_OK; 
     } 
    }; 

    class SessionNotification : public IAudioSessionNotification { 

    private: 
     LONG rc; 
     ~SessionNotification() {} 

    public: 
     SessionNotification() : rc(1) {} 

     ULONG STDMETHODCALLTYPE AddRef() { 
     return InterlockedIncrement(&rc); 
     } 

     ULONG STDMETHODCALLTYPE Release() { 
     ULONG rc = InterlockedDecrement(&this->rc); 
     if(rc == 0) 
      delete this; 
     return rc; 
     } 

     HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void** ppv) { 
     if(IID_IUnknown == riid) { 
      AddRef(); 
      *ppv = static_cast<IUnknown*>(this); 
      return S_OK; 
     } 
     else if(__uuidof(IAudioSessionNotification) == riid) { 
      AddRef(); 
      *ppv = static_cast<IAudioSessionNotification*>(this); 
      return S_OK; 
     } 
     else { 
      *ppv = nullptr; 
      return E_NOINTERFACE; 
     } 
     } 

     HRESULT OnSessionCreated(IAudioSessionControl *newSession) { 
     HRESULT hr = S_OK; 
     if(newSession) { 
      newSession->AddRef(); 
      CComHeapPtr<WCHAR> name; 
      ULONG id = InterlockedIncrement(&sessionId); 
      VERIFY(newSession->GetDisplayName(&name)); 
      std::wcout << L"created session # " << id << L": " << name.m_pData << L"\n"; 
      VERIFY(newSession->RegisterAudioSessionNotification(new SessionEvents(id))); 
     } 
     error: 
     return hr; 
     } 
    }; 

    int main(int argc, char** argv) { 

     HRESULT hr = S_OK; 
     VERIFY(CoInitializeEx(nullptr, COINIT_MULTITHREADED)); 

     { 
     int count; 
     std::wstring line; 
     CComPtr<IMMDevice> defaultOutput; 
     CComPtr<IMMDeviceEnumerator> devices; 
     CComPtr<IAudioSessionManager2> manager; 
     CComPtr<IAudioSessionEnumerator> sessions; 
     SessionNotification* notification = new SessionNotification; 

     VERIFY(devices.CoCreateInstance(__uuidof(MMDeviceEnumerator))); 
     VERIFY(devices->GetDefaultAudioEndpoint(eRender, eMultimedia, &defaultOutput)); 
     VERIFY(defaultOutput->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, nullptr, reinterpret_cast<void**>(&manager))); 
     VERIFY(manager->RegisterSessionNotification(notification)); 
     VERIFY(manager->GetSessionEnumerator(&sessions)); 
     VERIFY(sessions->GetCount(&count)); 
     std::wcout << L"Initial sessions: " << count << L"\n"; 
     for(int s = 0; s < count; s++) { 
      AudioSessionState state; 
      CComHeapPtr<WCHAR> name; 
      CComPtr<IAudioSessionControl> control; 
      VERIFY(sessions->GetSession(s, &control)); 
      VERIFY(control->GetDisplayName(&name)); 
      VERIFY(control->GetState(&state)); 
      std::wcout << L"Initial session name: " << name.m_pData << L", active = " << (state == AudioSessionStateActive) << L"\n"; 
      VERIFY(control->RegisterAudioSessionNotification(new SessionEvents(InterlockedIncrement(&sessionId)))); 
     } 

     std::wcout << L"Press return to exit...\n"; 
     std::getline(std::wcin, line); 
     VERIFY(manager->UnregisterSessionNotification(notification)); 
     } 

    error: 
     CoUninitialize(); 
     return 0; 
    }