Audacity  2.2.2
DeviceChange.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  DeviceChange.cpp
6 
7  Leland Lucius
8 
9 *******************************************************************//*******************************************************************/
15 
16 #include "DeviceChange.h"
17 
18 #include "Experimental.h"
19 
20 #if defined(EXPERIMENTAL_DEVICE_CHANGE_HANDLER)
21 
22 #if defined(HAVE_DEVICE_CHANGE)
23 
24 #include <wx/module.h>
25 #include <wx/timer.h>
26 #include <wx/thread.h>
27 
28 DECLARE_LOCAL_EVENT_TYPE(EVT_DEVICE_CHANGE, -1);
29 DEFINE_EVENT_TYPE(EVT_DEVICE_CHANGE);
30 
31 #if defined(__WXMSW__)
32 
33 #include <Windows.h>
34 #include <mmsystem.h>
35 #include <mmdeviceapi.h>
36 #include <audioclient.h>
37 
38 class DeviceChangeListener final : public IMMNotificationClient,
39  public DeviceChangeInterface
40 {
41 public:
42  DeviceChangeListener()
43  {
44  mRefCnt = 1;
45  mEnumerator = NULL;
46  mEnabled = false;
47  mHandler = NULL;
48  }
49 
50  virtual ~DeviceChangeListener()
51  {
52  if (mEnumerator)
53  {
54  mEnumerator->UnregisterEndpointNotificationCallback(this);
55  mEnumerator = NULL;
56  }
57 
58  if (mHandler)
59  {
60  CoInitialize(NULL);
61  }
62  }
63 
64  // IUnknown implementation
65 
66  ULONG STDMETHODCALLTYPE AddRef()
67  {
68  return InterlockedIncrement(&mRefCnt);
69  }
70 
71  ULONG STDMETHODCALLTYPE Release()
72  {
73  ULONG cnt = InterlockedDecrement(&mRefCnt);
74  if (cnt == 0)
75  {
76  delete this;
77  }
78  return cnt;
79  }
80 
81  HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface)
82  {
83  if (riid == IID_IUnknown)
84  {
85  AddRef();
86  *ppvInterface = (IUnknown *) this;
87  }
88  else if (riid == __uuidof(IMMNotificationClient))
89  {
90  AddRef();
91  *ppvInterface = (IMMNotificationClient *) this;
92  }
93  else
94  {
95  *ppvInterface = NULL;
96  return E_NOINTERFACE;
97  }
98 
99  return S_OK;
100  }
101 
102  // IMMDeviceChangeListener implementation
103 
104  HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged( EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId)
105  {
106  return S_OK;
107  }
108 
109  HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId)
110  {
111  return S_OK;
112  }
113 
114  HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId)
115  {
116  return S_OK;
117  }
118 
119  HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(LPCWSTR pwstrDeviceId, DWORD dwNewState)
120  {
121  if (mEnabled)
122  {
123  mEnabled = false;
124  wxMutexGuiEnter();
125  wxCommandEvent e(EVT_DEVICE_CHANGE);
126  mHandler->AddPendingEvent(e);
127  wxMutexGuiLeave();
128  }
129 
130  return S_OK;
131  }
132 
133  HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(LPCWSTR pwstrDeviceId, const PROPERTYKEY key)
134  {
135  return S_OK;
136  }
137 
138  bool SetHandler(wxEvtHandler *handler)
139  {
140  mHandler = handler;
141 
142  CoInitialize(NULL);
143 
144  HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
145  NULL,
146  CLSCTX_INPROC_SERVER,
147  __uuidof(IMMDeviceEnumerator),
148  (void**)&mEnumerator);
149  if (hr == S_OK && mEnumerator)
150  {
151  mEnumerator->RegisterEndpointNotificationCallback(this);
152  }
153 
154  return hr == S_OK && mEnumerator;
155  }
156 
157  void Enable(bool enable)
158  {
159  mEnabled = enable;
160  }
161 
162 private:
163  wxEvtHandler *mHandler;
164  bool mEnabled;
165  ULONG mRefCnt;
166  IMMDeviceEnumerator *mEnumerator;
167 };
168 
169 #elif defined(__WXGTK__)
170 
171 #include <libudev.h>
172 #include <stdio.h>
173 #include <stdlib.h>
174 #include <locale.h>
175 #include <unistd.h>
176 
177 class DeviceChangeListener final : public DeviceChangeInterface
178 {
179 public:
180  DeviceChangeListener()
181  {
182  mEnabled = false;
183  mHandler = NULL;
184  mThread = 0;
185  }
186 
187  virtual ~DeviceChangeListener()
188  {
189  if (mThread)
190  {
191  pthread_cancel(mThread);
192  mThread = 0;
193  }
194  }
195 
196  // IUnknown implementation
197  bool SetHandler(wxEvtHandler *handler)
198  {
199  mHandler = handler;
200 
201  return pthread_create(&mThread, NULL, DeviceChangeListener::Listener, this) == 0;
202  }
203 
204  void Enable(bool enable)
205  {
206  mEnabled = enable;
207  }
208 
209  static void *Listener(void *parm)
210  {
211  DeviceChangeListener *This = (DeviceChangeListener *) parm;
212 
213  // Instantiate the udev object
214  struct udev *udev = udev_new();
215  if (!udev)
216  {
217  pthread_exit(NULL);
218  }
219 
220  // Instantiate the monitor object
221  struct udev_monitor *mon = udev_monitor_new_from_netlink(udev, "udev");
222 
223  // Start receiving notifications
224  udev_monitor_enable_receiving(mon);
225 
226  // Get the file descriptor we'll wait on
227  int fd = udev_monitor_get_fd(mon);
228 
229  while (true)
230  {
231  fd_set set;
232 
233  FD_ZERO(&set);
234  FD_SET(fd, &set);
235 
236  if (select(fd + 1, &set, NULL, NULL, NULL) < 0)
237  {
238  break;
239  }
240 
241  if (FD_ISSET(fd, &set))
242  {
243  struct udev_device *dev = udev_monitor_receive_device(mon);
244  if (dev)
245  {
246 #if 0
247  wxPrintf("Got Device\n");
248  wxPrintf(" Node: %s\n", udev_device_get_devnode(dev));
249  wxPrintf(" Subsystem: %s\n", udev_device_get_subsystem(dev));
250  wxPrintf(" Devtype: %s\n", udev_device_get_devtype(dev));
251  wxPrintf(" Action: %s\n", udev_device_get_action(dev));
252 #endif
253  if (This->mEnabled)
254  {
255  This->mEnabled = false;
256  wxMutexGuiEnter();
257  wxCommandEvent e(EVT_DEVICE_CHANGE);
258  This->mHandler->AddPendingEvent(e);
259  wxMutexGuiLeave();
260  }
261 
262  udev_device_unref(dev);
263  }
264  }
265  }
266 
267  udev_unref(udev);
268 
269  pthread_exit(NULL);
270  }
271 
272 private:
273  wxEvtHandler *mHandler;
274  bool mEnabled;
275  pthread_t mThread;
276 };
277 
278 #elif defined(__WXMAC__)
279 
280 #include <CoreAudio/CoreAudio.h>
281 
282 class DeviceChangeListener final : public DeviceChangeInterface
283 {
284 public:
285  DeviceChangeListener()
286  {
287  mEnabled = false;
288  mHandler = NULL;
289  mListening = false;
290  }
291 
292  virtual ~DeviceChangeListener()
293  {
294  if (mListening)
295  {
296  AudioObjectPropertyAddress property_address;
297 
298  property_address.mSelector = kAudioHardwarePropertyDevices;
299  property_address.mScope = kAudioObjectPropertyScopeGlobal;
300  property_address.mElement = kAudioObjectPropertyElementMaster;
301 
302  AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
303  &property_address,
304  DeviceChangeListener::Listener,
305  this);
306  mListening = false;
307  }
308  }
309 
310  // IUnknown implementation
311  bool SetHandler(wxEvtHandler *handler)
312  {
313  mHandler = handler;
314 
315  AudioObjectPropertyAddress property_address;
316 
317  property_address.mSelector = kAudioHardwarePropertyDevices;
318  property_address.mScope = kAudioObjectPropertyScopeGlobal;
319  property_address.mElement = kAudioObjectPropertyElementMaster;
320 
321  mListening = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
322  &property_address,
323  DeviceChangeListener::Listener,
324  this) == 0;
325  }
326 
327  void Enable(bool enable)
328  {
329  mEnabled = enable;
330  }
331 
332  static OSStatus Listener(AudioObjectID objectID,
333  UInt32 numberAddresses,
334  const AudioObjectPropertyAddress inAddresses[],
335  void *clientData)
336  {
337  DeviceChangeListener *This = (DeviceChangeListener *) clientData;
338 
339  for (int i = 0; i < numberAddresses; i++)
340  {
341 #if 0
342  wxPrintf("address %d\n", i);
343  wxPrintf("selector %08x\n", inAddresses[i].mSelector);
344  wxPrintf("scope %08x\n", inAddresses[i].mScope);
345  wxPrintf("element %08x\n", inAddresses[i].mElement);
346 #endif
347  if (This->mEnabled)
348  {
349  This->mEnabled = false;
350  wxMutexGuiEnter();
351  wxCommandEvent e(EVT_DEVICE_CHANGE);
352  This->mHandler->AddPendingEvent(e);
353  wxMutexGuiLeave();
354  }
355  }
356 
357  return 0;
358  }
359 
360 private:
361  wxEvtHandler *mHandler;
362  bool mEnabled;
363  bool mListening;
364 };
365 #endif
366 
367 BEGIN_EVENT_TABLE(DeviceChangeHandler, wxEvtHandler)
368  EVT_COMMAND(wxID_ANY, EVT_DEVICE_CHANGE, DeviceChangeHandler::OnChange)
369  EVT_TIMER(wxID_ANY, DeviceChangeHandler::OnTimer)
371 
372 DeviceChangeHandler::DeviceChangeHandler()
373 : wxEvtHandler()
374 {
375  mTimer.SetOwner(this);
376  mListener = std::make_unique<DeviceChangeListener>();
377  mListener->SetHandler(this);
378  mListener->Enable(true);
379 }
380 
381 DeviceChangeHandler::~DeviceChangeHandler()
382 {
383  if (mListener)
384  mListener->Enable(false);
385 }
386 
387 void DeviceChangeHandler::Enable(bool enable)
388 {
389  mListener->Enable(enable);
390 }
391 
392 void DeviceChangeHandler::OnChange(wxCommandEvent & evt)
393 {
394  mTimer.Start(500, true);
395 }
396 
397 void DeviceChangeHandler::OnTimer(wxTimerEvent & evt)
398 {
399  DeviceChangeNotification();
400  mListener->Enable(true);
401 }
402 
403 #endif
404 
405 #endif
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:89
DEFINE_EVENT_TYPE(EVT_OPEN_AUDIO_FILE)
Custom events.
END_EVENT_TABLE()