audacia/src/DeviceChange.cpp

406 lines
9.2 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
DeviceChange.cpp
Leland Lucius
*******************************************************************//*!
\file DeviceChange.cpp
\brief
*//*******************************************************************/
#include "DeviceChange.h"
#if defined(EXPERIMENTAL_DEVICE_CHANGE_HANDLER)
#if defined(HAVE_DEVICE_CHANGE)
#include <wx/module.h>
#include <wx/timer.h>
#include <wx/thread.h>
DECLARE_LOCAL_EVENT_TYPE(EVT_DEVICE_CHANGE, -1);
DEFINE_EVENT_TYPE(EVT_DEVICE_CHANGE);
#if defined(__WXMSW__)
#include <Windows.h>
#include <mmsystem.h>
#include <mmdeviceapi.h>
#include <audioclient.h>
class DeviceChangeListener final : public IMMNotificationClient,
public DeviceChangeInterface
{
public:
DeviceChangeListener()
{
mRefCnt = 1;
mEnumerator = NULL;
mEnabled = false;
mHandler = NULL;
}
virtual ~DeviceChangeListener()
{
if (mEnumerator)
{
mEnumerator->UnregisterEndpointNotificationCallback(this);
mEnumerator = NULL;
}
if (mHandler)
{
CoInitialize(NULL);
}
}
// IUnknown implementation
ULONG STDMETHODCALLTYPE AddRef()
{
return InterlockedIncrement(&mRefCnt);
}
ULONG STDMETHODCALLTYPE Release()
{
ULONG cnt = InterlockedDecrement(&mRefCnt);
if (cnt == 0)
{
delete this;
}
return cnt;
}
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface)
{
if (riid == IID_IUnknown)
{
AddRef();
*ppvInterface = (IUnknown *) this;
}
else if (riid == __uuidof(IMMNotificationClient))
{
AddRef();
*ppvInterface = (IMMNotificationClient *) this;
}
else
{
*ppvInterface = NULL;
return E_NOINTERFACE;
}
return S_OK;
}
// IMMDeviceChangeListener implementation
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged( EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
{
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)
{
if (mEnabled)
{
mEnabled = false;
wxMutexGuiEnter();
wxCommandEvent e(EVT_DEVICE_CHANGE);
mHandler->AddPendingEvent(e);
wxMutexGuiLeave();
}
return S_OK;
}
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
{
return S_OK;
}
bool SetHandler(wxEvtHandler *handler)
{
mHandler = handler;
CoInitialize(NULL);
HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IMMDeviceEnumerator),
(void**)&mEnumerator);
if (hr == S_OK && mEnumerator)
{
mEnumerator->RegisterEndpointNotificationCallback(this);
}
return hr == S_OK && mEnumerator;
}
void Enable(bool enable)
{
mEnabled = enable;
}
private:
wxEvtHandler *mHandler;
bool mEnabled;
ULONG mRefCnt;
IMMDeviceEnumerator *mEnumerator;
};
#elif defined(__WXGTK__)
#include <libudev.h>
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <unistd.h>
class DeviceChangeListener final : public DeviceChangeInterface
{
public:
DeviceChangeListener()
{
mEnabled = false;
mHandler = NULL;
mThread = 0;
}
virtual ~DeviceChangeListener()
{
if (mThread)
{
pthread_cancel(mThread);
mThread = 0;
}
}
// IUnknown implementation
bool SetHandler(wxEvtHandler *handler)
{
mHandler = handler;
return pthread_create(&mThread, NULL, DeviceChangeListener::Listener, this) == 0;
}
void Enable(bool enable)
{
mEnabled = enable;
}
static void *Listener(void *parm)
{
DeviceChangeListener *This = (DeviceChangeListener *) parm;
// Instantiate the udev object
struct udev *udev = udev_new();
if (!udev)
{
pthread_exit(NULL);
}
// Instantiate the monitor object
struct udev_monitor *mon = udev_monitor_new_from_netlink(udev, "udev");
// Start receiving notifications
udev_monitor_enable_receiving(mon);
// Get the file descriptor we'll wait on
int fd = udev_monitor_get_fd(mon);
while (true)
{
fd_set set;
FD_ZERO(&set);
FD_SET(fd, &set);
if (select(fd + 1, &set, NULL, NULL, NULL) < 0)
{
break;
}
if (FD_ISSET(fd, &set))
{
struct udev_device *dev = udev_monitor_receive_device(mon);
if (dev)
{
#if 0
wxPrintf("Got Device\n");
wxPrintf(" Node: %s\n", udev_device_get_devnode(dev));
wxPrintf(" Subsystem: %s\n", udev_device_get_subsystem(dev));
wxPrintf(" Devtype: %s\n", udev_device_get_devtype(dev));
wxPrintf(" Action: %s\n", udev_device_get_action(dev));
#endif
if (This->mEnabled)
{
This->mEnabled = false;
wxMutexGuiEnter();
wxCommandEvent e(EVT_DEVICE_CHANGE);
This->mHandler->AddPendingEvent(e);
wxMutexGuiLeave();
}
udev_device_unref(dev);
}
}
}
udev_unref(udev);
pthread_exit(NULL);
}
private:
wxEvtHandler *mHandler;
bool mEnabled;
pthread_t mThread;
};
#elif defined(__WXMAC__)
#include <CoreAudio/CoreAudio.h>
class DeviceChangeListener final : public DeviceChangeInterface
{
public:
DeviceChangeListener()
{
mEnabled = false;
mHandler = NULL;
mListening = false;
}
virtual ~DeviceChangeListener()
{
if (mListening)
{
AudioObjectPropertyAddress property_address;
property_address.mSelector = kAudioHardwarePropertyDevices;
property_address.mScope = kAudioObjectPropertyScopeGlobal;
property_address.mElement = kAudioObjectPropertyElementMaster;
AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
&property_address,
DeviceChangeListener::Listener,
this);
mListening = false;
}
}
// IUnknown implementation
bool SetHandler(wxEvtHandler *handler)
{
mHandler = handler;
AudioObjectPropertyAddress property_address;
property_address.mSelector = kAudioHardwarePropertyDevices;
property_address.mScope = kAudioObjectPropertyScopeGlobal;
property_address.mElement = kAudioObjectPropertyElementMaster;
mListening = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
&property_address,
DeviceChangeListener::Listener,
this) == 0;
}
void Enable(bool enable)
{
mEnabled = enable;
}
static OSStatus Listener(AudioObjectID objectID,
UInt32 numberAddresses,
const AudioObjectPropertyAddress inAddresses[],
void *clientData)
{
DeviceChangeListener *This = (DeviceChangeListener *) clientData;
for (int i = 0; i < numberAddresses; i++)
{
#if 0
wxPrintf("address %d\n", i);
wxPrintf("selector %08x\n", inAddresses[i].mSelector);
wxPrintf("scope %08x\n", inAddresses[i].mScope);
wxPrintf("element %08x\n", inAddresses[i].mElement);
#endif
if (This->mEnabled)
{
This->mEnabled = false;
wxMutexGuiEnter();
wxCommandEvent e(EVT_DEVICE_CHANGE);
This->mHandler->AddPendingEvent(e);
wxMutexGuiLeave();
}
}
return 0;
}
private:
wxEvtHandler *mHandler;
bool mEnabled;
bool mListening;
};
#endif
BEGIN_EVENT_TABLE(DeviceChangeHandler, wxEvtHandler)
EVT_COMMAND(wxID_ANY, EVT_DEVICE_CHANGE, DeviceChangeHandler::OnChange)
EVT_TIMER(wxID_ANY, DeviceChangeHandler::OnTimer)
END_EVENT_TABLE()
DeviceChangeHandler::DeviceChangeHandler()
: wxEvtHandler()
{
mTimer.SetOwner(this);
mListener = std::make_unique<DeviceChangeListener>();
mListener->SetHandler(this);
mListener->Enable(true);
}
DeviceChangeHandler::~DeviceChangeHandler()
{
if (mListener)
mListener->Enable(false);
}
void DeviceChangeHandler::Enable(bool enable)
{
mListener->Enable(enable);
}
void DeviceChangeHandler::OnChange(wxCommandEvent & evt)
{
mTimer.Start(500, true);
}
void DeviceChangeHandler::OnTimer(wxTimerEvent & evt)
{
DeviceChangeNotification();
mListener->Enable(true);
}
#endif
#endif