wasapi:检测到没有播放任何内容(设备未使用)

时间:2016-07-11 15:14:20

标签: audio wav audio-player wasapi

我使用wasapi进行环回捕获。之后的所有数据都保存到wav文件中。 当它开始捕获时,它会一直传送数据,直到我停止捕获过程,即使没有应用程序使用音频设备,也没有人产生音乐。因此,我写入文件不是有价值的数据 - 只是沉默。

因此,当设备完全没有使用时,有没有办法让当前播放的音乐和情况明显不同。在以后的情况下,我想中断将数据记录到文件中的过程,并在再次通过音频设备播放smth时创建新的过程。

PS AudioCaptureClinet方法GetBuffer以输出参数flag为特色,在某些条件下看似可能有值AUDCLNT_BUFFERFLAGS_SILENT == 0x1但在我的情况下每次都会返回flag==0

1 个答案:

答案 0 :(得分:1)

好的,事实证明没有可靠的方法从环回流本身推断没有任何东西播放到相应的输出设备。但是,可以在输出设备本身和每个会话上查询音频会话,检查它的状态是否处于活动状态。当会话添加到设备或从设备中删除会话时,也可以收到通知,并在会话(de)激活时得到通知。这样,您可以在第一个会话在设备上激活时启动环回流,并在最后一个会话停用时停止。

以下是如何操作:

  • 实施IAudioSessionNotification和IAudioSessionEvents
  • 设置MTA线程,STA将无法正常工作
  • 检索要监控的输出的IMMDevice接口
  • 从IMMDevice获取IAudioSessionManager2
  • 从IAudioSessionManager2获取IAudioSessionEnumerator
  • 从IAudioSessionEnumerator中,枚举IAudioSessionControls
    • 这是初始会话列表。它将在调查员的整个生命周期内保持不变。
    • 从IAudioSessionControl,使用GetState查看最初活动的内容
    • 使用IAudioSessionControl :: RegisterAudioSessionNotification获取(de)激活初始会话的通知
  • 使用IAudioSessionManager2 :: RegisterSessionNotification注册自定义会话通知
    • 在初始枚举后创建的任何其他会话将调用此方法
    • 从IAudioSessionNotification :: OnSessionCreated,使用IAudioSessionControl :: RegisterAudioSessionNotification获取(de)激活其他会话的通知
  • 如果任何会话最初处于活动状态,请启动环回流
  • 当最后一个(初始或额外)会话被取消激活或断开连接时,请停止环回流
  • 当然会在任何会话再次激活时重新启动

以下是一个工作示例。请注意,我不是:

  • 除了打印出错之外,还要进行彻底的错误处理
  • 实际设置环回流。示例仅在任何(de)激活时打印。
  • 对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;
    }