我使用wasapi
进行环回捕获。之后的所有数据都保存到wav
文件中。
当它开始捕获时,它会一直传送数据,直到我停止捕获过程,即使没有应用程序使用音频设备,也没有人产生音乐。因此,我写入文件不是有价值的数据 - 只是沉默。
因此,当设备完全没有使用时,有没有办法让当前播放的音乐和情况明显不同。在以后的情况下,我想中断将数据记录到文件中的过程,并在再次通过音频设备播放smth时创建新的过程。
PS AudioCaptureClinet方法GetBuffer
以输出参数flag
为特色,在某些条件下看似可能有值AUDCLNT_BUFFERFLAGS_SILENT == 0x1
但在我的情况下每次都会返回flag==0
答案 0 :(得分:1)
好的,事实证明没有可靠的方法从环回流本身推断没有任何东西播放到相应的输出设备。但是,可以在输出设备本身和每个会话上查询音频会话,检查它的状态是否处于活动状态。当会话添加到设备或从设备中删除会话时,也可以收到通知,并在会话(de)激活时得到通知。这样,您可以在第一个会话在设备上激活时启动环回流,并在最后一个会话停用时停止。
以下是如何操作:
以下是一个工作示例。请注意,我不是:
#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;
}