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