mirror of
https://github.com/clangen/musikcube.git
synced 2025-03-14 13:21:13 +00:00
Removed 'IMMNotificationClient' implementation of audio endpoint routing,
go with a polling method instead. There are bugs with IMMNotificationClient with no apparent solution.
This commit is contained in:
parent
2495fd97d4
commit
978ad2ec9d
@ -34,6 +34,7 @@
|
||||
|
||||
#include "WasapiOut.h"
|
||||
#include <core/sdk/constants.h>
|
||||
#include <core/sdk/IPreferences.h>
|
||||
#include <AudioSessionTypes.h>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
@ -41,6 +42,7 @@
|
||||
#include <algorithm>
|
||||
|
||||
#define MAX_BUFFERS_PER_OUTPUT 16
|
||||
#define DEFAULT_DEVICE_CHECK_INTERVAL 2000
|
||||
|
||||
/* NOTE! device init and deinit logic was stolen and modified from
|
||||
QMMP's WASAPI output plugin! http://qmmp.ylsoftware.com/ */
|
||||
@ -50,83 +52,11 @@ QMMP's WASAPI output plugin! http://qmmp.ylsoftware.com/ */
|
||||
#endif
|
||||
|
||||
using Lock = std::unique_lock<std::recursive_mutex>;
|
||||
musik::core::sdk::IPreferences* prefs = nullptr;
|
||||
|
||||
class NotificationClient : public IMMNotificationClient {
|
||||
public:
|
||||
NotificationClient(WasapiOut* owner)
|
||||
: count(1)
|
||||
, owner(owner)
|
||||
, enumerator(nullptr) {
|
||||
}
|
||||
|
||||
~NotificationClient() {
|
||||
if (this->enumerator) {
|
||||
this->enumerator->Release();
|
||||
this->enumerator = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* IUnknown methods -- AddRef, Release, and QueryInterface */
|
||||
|
||||
ULONG STDMETHODCALLTYPE AddRef() {
|
||||
return InterlockedIncrement(&this->count);
|
||||
}
|
||||
|
||||
ULONG STDMETHODCALLTYPE Release() {
|
||||
ULONG newCount = InterlockedDecrement(&this->count);
|
||||
if (0 == newCount) {
|
||||
delete this;
|
||||
}
|
||||
return newCount;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) {
|
||||
if (IID_IUnknown == riid) {
|
||||
this->AddRef();
|
||||
*ppvInterface = (IUnknown*)this;
|
||||
}
|
||||
else if (__uuidof(IMMNotificationClient) == riid) {
|
||||
this->AddRef();
|
||||
*ppvInterface = (IMMNotificationClient*)this;
|
||||
}
|
||||
else {
|
||||
*ppvInterface = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/* Callback methods for device-event notifications. */
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(
|
||||
EDataFlow flow, ERole role,
|
||||
LPCWSTR pwstrDeviceId)
|
||||
{
|
||||
owner->OnDeviceChanged();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState) {
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key){
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
LONG count;
|
||||
IMMDeviceEnumerator *enumerator;
|
||||
WasapiOut* owner;
|
||||
};
|
||||
extern "C" __declspec(dllexport) void SetPreferences(musik::core::sdk::IPreferences* prefs) {
|
||||
::prefs = prefs;
|
||||
}
|
||||
|
||||
WasapiOut::WasapiOut()
|
||||
: enumerator(nullptr)
|
||||
@ -140,8 +70,9 @@ WasapiOut::WasapiOut()
|
||||
, outputBufferFrames(0)
|
||||
, state(StateStopped)
|
||||
, latency(0)
|
||||
, deviceChanged(false)
|
||||
, volume(1.0f) {
|
||||
, volume(1.0f)
|
||||
, lastDeviceCheck(0)
|
||||
, enableEndpointRouting(0) {
|
||||
ZeroMemory(&waveFormat, sizeof(WAVEFORMATEXTENSIBLE));
|
||||
}
|
||||
|
||||
@ -226,6 +157,27 @@ void WasapiOut::Drain() {
|
||||
}
|
||||
}
|
||||
|
||||
/* ideally we'd use a IMMNotificationClient callback, but it seems to
|
||||
introduce random crashing when devices are attached /detached from the
|
||||
system... but only sometimes. there are similar crashes in firefox,
|
||||
VLC, and some other apps, but no known resolution. instead we poll the
|
||||
default device every few seconds. */
|
||||
bool WasapiOut::DefaultDeviceChanged() {
|
||||
IMMDevice* currentDevice;
|
||||
if (this->enumerator && this->enumerator->GetDefaultAudioEndpoint(eRender, eConsole, ¤tDevice) == S_OK) {
|
||||
wchar_t* dev1 = nullptr;
|
||||
wchar_t* dev2 = nullptr;
|
||||
this->device->GetId(&dev1);
|
||||
currentDevice->GetId(&dev2);
|
||||
bool changed = lstrcmpW(dev1, dev2) != 0;
|
||||
CoTaskMemFree(dev1);
|
||||
CoTaskMemFree(dev2);
|
||||
currentDevice->Release();
|
||||
return changed;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int WasapiOut::Play(IBuffer *buffer, IBufferProvider *provider) {
|
||||
Lock lock(this->stateMutex);
|
||||
|
||||
@ -233,10 +185,15 @@ int WasapiOut::Play(IBuffer *buffer, IBufferProvider *provider) {
|
||||
return OutputInvalidState;
|
||||
}
|
||||
|
||||
if (this->deviceChanged) {
|
||||
this->Reset();
|
||||
this->deviceChanged = false;
|
||||
return OutputFormatError;
|
||||
if (this->enableEndpointRouting) {
|
||||
DWORD now = GetTickCount();
|
||||
if (now - this->lastDeviceCheck > DEFAULT_DEVICE_CHECK_INTERVAL) {
|
||||
lastDeviceCheck = now;
|
||||
if (this->DefaultDeviceChanged()) {
|
||||
this->Reset();
|
||||
return OutputFormatError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!this->Configure(buffer)) {
|
||||
@ -274,6 +231,10 @@ int WasapiOut::Play(IBuffer *buffer, IBufferProvider *provider) {
|
||||
}
|
||||
|
||||
void WasapiOut::Reset() {
|
||||
if (this->audioClient) {
|
||||
this->audioClient->Stop();
|
||||
}
|
||||
|
||||
if (this->simpleAudioVolume) {
|
||||
this->simpleAudioVolume->Release();
|
||||
this->simpleAudioVolume = nullptr;
|
||||
@ -302,16 +263,10 @@ void WasapiOut::Reset() {
|
||||
|
||||
if (this->device) {
|
||||
this->device->Release();
|
||||
this->device = 0;
|
||||
this->device = nullptr;
|
||||
}
|
||||
|
||||
if (this->enumerator) {
|
||||
if (this->notificationClient) {
|
||||
this->enumerator->UnregisterEndpointNotificationCallback(this->notificationClient);
|
||||
this->notificationClient->Release();
|
||||
this->notificationClient = nullptr;
|
||||
}
|
||||
|
||||
this->enumerator->Release();
|
||||
this->enumerator = nullptr;
|
||||
}
|
||||
@ -344,12 +299,6 @@ bool WasapiOut::Configure(IBuffer *buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this->notificationClient = new NotificationClient(this);
|
||||
|
||||
if ((result = this->enumerator->RegisterEndpointNotificationCallback(this->notificationClient)) != S_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((result = this->device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**) &this->audioClient)) != S_OK) {
|
||||
return false;
|
||||
}
|
||||
@ -361,6 +310,10 @@ bool WasapiOut::Configure(IBuffer *buffer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (::prefs) {
|
||||
this->enableEndpointRouting = prefs->GetBool("enable_audio_endpoint_routing", true);
|
||||
}
|
||||
|
||||
DWORD speakerConfig = 0;
|
||||
switch (buffer->Channels()) {
|
||||
case 1:
|
||||
|
@ -68,8 +68,6 @@ class WasapiOut : public IOutput {
|
||||
virtual double Latency();
|
||||
virtual void Drain();
|
||||
|
||||
void OnDeviceChanged() { this->deviceChanged = true; }
|
||||
|
||||
private:
|
||||
enum State {
|
||||
StateStopped,
|
||||
@ -78,6 +76,7 @@ class WasapiOut : public IOutput {
|
||||
};
|
||||
|
||||
bool Configure(IBuffer *buffer);
|
||||
bool DefaultDeviceChanged();
|
||||
void Reset();
|
||||
|
||||
IMMDeviceEnumerator *enumerator;
|
||||
@ -94,6 +93,7 @@ class WasapiOut : public IOutput {
|
||||
double volume;
|
||||
double latency;
|
||||
int rate;
|
||||
bool deviceChanged;
|
||||
DWORD lastDeviceCheck;
|
||||
bool enableEndpointRouting;
|
||||
std::recursive_mutex stateMutex;
|
||||
};
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include <core/sdk/constants.h>
|
||||
#include <core/sdk/IPlugin.h>
|
||||
#include <core/sdk/IPreferences.h>
|
||||
#include "WasapiOut.h"
|
||||
|
||||
class WasapiPlugin : public musik::core::sdk::IPlugin {
|
||||
@ -58,3 +59,5 @@ extern "C" __declspec(dllexport) musik::core::sdk::IPlugin* GetPlugin() {
|
||||
extern "C" __declspec(dllexport) musik::core::sdk::IOutput* GetAudioOutput() {
|
||||
return new WasapiOut();
|
||||
}
|
||||
|
||||
extern "C" __declspec(dllexport) void SetPreferences(musik::core::sdk::IPreferences* prefs);
|
Loading…
x
Reference in New Issue
Block a user