audacia/src/DeviceChange.cpp
Paul Licameli 7824e94030 Harmlessly qualify classes as final (or explicitly comment not)...
... Should have no effect on generated code, except perhaps some slight faster
virtual function calls.  Mostly useful as documentation of design intent.

Tried to mark every one of our classes that inherits from another, or is a
base for others, or has abstract virtual functions, and a few others besides.
2016-02-24 20:58:30 -05:00

409 lines
9.2 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
DeviceChange.cpp
Leland Lucius
*******************************************************************//*!
\file DeviceChange.cpp
\brief
*//*******************************************************************/
#include "DeviceChange.h"
#include "Experimental.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
printf("Got Device\n");
printf(" Node: %s\n", udev_device_get_devnode(dev));
printf(" Subsystem: %s\n", udev_device_get_subsystem(dev));
printf(" Devtype: %s\n", udev_device_get_devtype(dev));
printf(" 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
printf("address %d\n", i);
printf("selector %08x\n", inAddresses[i].mSelector);
printf("scope %08x\n", inAddresses[i].mScope);
printf("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 = new DeviceChangeListener();
mListener->SetHandler(this);
mListener->Enable(true);
}
DeviceChangeHandler::~DeviceChangeHandler()
{
if (mListener)
{
mListener->Enable(false);
delete mListener;
}
}
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