mirror of
https://github.com/cookiengineer/audacity
synced 2025-05-06 14:52:34 +02:00
... 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.
409 lines
9.2 KiB
C++
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
|