2 This file is part of PulseAudio.
4 Copyright 2009,2010 Daniel Mack <daniel@caiaq.de>
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <pulse/xmalloc.h>
28 #include <pulsecore/module.h>
29 #include <pulsecore/core-util.h>
30 #include <pulsecore/modargs.h>
31 #include <pulsecore/log.h>
32 #include <pulsecore/llist.h>
34 #include <CoreAudio/CoreAudio.h>
36 #include "module-coreaudio-detect-symdef.h"
38 #define DEVICE_MODULE_NAME "module-coreaudio-device"
40 PA_MODULE_AUTHOR("Daniel Mack");
41 PA_MODULE_DESCRIPTION("CoreAudio device detection");
42 PA_MODULE_VERSION(PACKAGE_VERSION
);
43 PA_MODULE_LOAD_ONCE(TRUE
);
46 typedef struct ca_device ca_device
;
50 unsigned int module_index
;
51 PA_LLIST_FIELDS(ca_device
);
56 pa_io_event
*detect_io
;
58 PA_LLIST_HEAD(ca_device
, devices
);
61 static int ca_device_added(struct pa_module
*m
, AudioObjectID id
) {
62 AudioObjectPropertyAddress property_address
;
66 struct ca_device
*dev
;
71 pa_assert_se(u
= m
->userdata
);
73 /* To prevent generating a black hole that will suck us in,
74 don't create sources/sinks for PulseAudio virtual devices */
76 property_address
.mSelector
= kAudioDevicePropertyDeviceManufacturer
;
77 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
78 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
81 err
= AudioObjectGetPropertyData(id
, &property_address
, 0, NULL
, &size
, tmp
);
83 if (!err
&& strcmp(tmp
, "pulseaudio.org") == 0)
86 args
= pa_sprintf_malloc("object_id=%d", (int) id
);
87 pa_log_debug("Loading %s with arguments '%s'", DEVICE_MODULE_NAME
, args
);
88 mod
= pa_module_load(m
->core
, DEVICE_MODULE_NAME
, args
);
92 pa_log_info("Failed to load module %s with arguments '%s'", DEVICE_MODULE_NAME
, args
);
96 dev
= pa_xnew0(ca_device
, 1);
97 dev
->module_index
= mod
->index
;
100 PA_LLIST_INIT(ca_device
, dev
);
101 PA_LLIST_PREPEND(ca_device
, u
->devices
, dev
);
106 static int ca_update_device_list(struct pa_module
*m
) {
107 AudioObjectPropertyAddress property_address
;
109 UInt32 i
, size
, num_devices
;
110 AudioDeviceID
*device_id
;
111 struct ca_device
*dev
;
115 pa_assert_se(u
= m
->userdata
);
117 property_address
.mSelector
= kAudioHardwarePropertyDevices
;
118 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
119 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
121 /* get the number of currently available audio devices */
122 err
= AudioObjectGetPropertyDataSize(kAudioObjectSystemObject
, &property_address
, 0, NULL
, &size
);
124 pa_log("Unable to get data size for kAudioHardwarePropertyDevices.");
128 num_devices
= size
/ sizeof(AudioDeviceID
);
129 device_id
= pa_xnew(AudioDeviceID
, num_devices
);
131 err
= AudioObjectGetPropertyData(kAudioObjectSystemObject
, &property_address
, 0, NULL
, &size
, device_id
);
133 pa_log("Unable to get kAudioHardwarePropertyDevices.");
138 /* scan for devices which are reported but not in our cached list */
139 for (i
= 0; i
< num_devices
; i
++) {
142 PA_LLIST_FOREACH(dev
, u
->devices
)
143 if (dev
->id
== device_id
[i
]) {
149 ca_device_added(m
, device_id
[i
]);
152 /* scan for devices which are in our cached list but are not reported */
155 PA_LLIST_FOREACH(dev
, u
->devices
) {
158 for (i
= 0; i
< num_devices
; i
++)
159 if (dev
->id
== device_id
[i
]) {
165 pa_log_debug("object id %d has been removed (module index %d) %p", (unsigned int) dev
->id
, dev
->module_index
, dev
);
166 pa_module_unload_request_by_index(m
->core
, dev
->module_index
, TRUE
);
167 PA_LLIST_REMOVE(ca_device
, u
->devices
, dev
);
169 /* the current list item pointer is not valid anymore, so start over. */
178 static OSStatus
property_listener_proc(AudioObjectID objectID
, UInt32 numberAddresses
,
179 const AudioObjectPropertyAddress inAddresses
[],
182 struct userdata
*u
= clientData
;
187 /* dispatch module load/unload operations in main thread */
188 write(u
->detect_fds
[1], &dummy
, 1);
193 static void detect_handle(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
194 pa_module
*m
= userdata
;
200 ca_update_device_list(m
);
203 int pa__init(pa_module
*m
) {
204 struct userdata
*u
= pa_xnew0(struct userdata
, 1);
205 AudioObjectPropertyAddress property_address
;
211 property_address
.mSelector
= kAudioHardwarePropertyDevices
;
212 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
213 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
215 if (AudioObjectAddPropertyListener(kAudioObjectSystemObject
, &property_address
, property_listener_proc
, u
)) {
216 pa_log("AudioObjectAddPropertyListener() failed.");
220 if (ca_update_device_list(m
))
223 pa_assert_se(pipe(u
->detect_fds
) == 0);
224 pa_assert_se(u
->detect_io
= m
->core
->mainloop
->io_new(m
->core
->mainloop
, u
->detect_fds
[0], PA_IO_EVENT_INPUT
, detect_handle
, m
));
233 void pa__done(pa_module
*m
) {
235 struct ca_device
*dev
;
236 AudioObjectPropertyAddress property_address
;
239 pa_assert_se(u
= m
->userdata
);
243 property_address
.mSelector
= kAudioHardwarePropertyDevices
;
244 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
245 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
247 AudioObjectRemovePropertyListener(kAudioObjectSystemObject
, &property_address
, property_listener_proc
, u
);
250 struct ca_device
*next
= dev
->next
;
252 pa_module_unload_request_by_index(m
->core
, dev
->module_index
, TRUE
);
258 if (u
->detect_fds
[0] >= 0)
259 close(u
->detect_fds
[0]);
261 if (u
->detect_fds
[1] >= 0)
262 close(u
->detect_fds
[1]);
265 m
->core
->mainloop
->io_free(u
->detect_io
);