1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00
audacity/src/DeviceChange.cpp
Paul Licameli e653b4aaf8 Eliminate Experimental.h, configure compile options instead...
... This makes it impossible to forget to include the EXPERIMENTAL definitions
(such as when cutting and pasting code) and so get unintended quiet changes of
behavior.

The EXPERIMENTAL flags are now specified instead in new file Experimental.cmake
2021-04-27 12:40:07 -04:00

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