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
);
44 PA_MODULE_USAGE("ioproc_frames=<passed on to module-coreaudio-device> ");
46 static const char* const valid_modargs
[] = {
51 typedef struct ca_device ca_device
;
55 unsigned int module_index
;
56 PA_LLIST_FIELDS(ca_device
);
61 pa_io_event
*detect_io
;
62 unsigned int ioproc_frames
;
63 PA_LLIST_HEAD(ca_device
, devices
);
66 static int ca_device_added(struct pa_module
*m
, AudioObjectID id
) {
67 AudioObjectPropertyAddress property_address
;
71 struct ca_device
*dev
;
76 pa_assert_se(u
= m
->userdata
);
78 /* To prevent generating a black hole that will suck us in,
79 don't create sources/sinks for PulseAudio virtual devices */
81 property_address
.mSelector
= kAudioDevicePropertyDeviceManufacturer
;
82 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
83 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
86 err
= AudioObjectGetPropertyData(id
, &property_address
, 0, NULL
, &size
, tmp
);
88 if (!err
&& strcmp(tmp
, "pulseaudio.org") == 0)
92 args
= pa_sprintf_malloc("object_id=%d ioproc_frames=%d", (int) id
, u
->ioproc_frames
);
94 args
= pa_sprintf_malloc("object_id=%d", (int) id
);
96 pa_log_debug("Loading %s with arguments '%s'", DEVICE_MODULE_NAME
, args
);
97 mod
= pa_module_load(m
->core
, DEVICE_MODULE_NAME
, args
);
101 pa_log_info("Failed to load module %s with arguments '%s'", DEVICE_MODULE_NAME
, args
);
105 dev
= pa_xnew0(ca_device
, 1);
106 dev
->module_index
= mod
->index
;
109 PA_LLIST_INIT(ca_device
, dev
);
110 PA_LLIST_PREPEND(ca_device
, u
->devices
, dev
);
115 static int ca_update_device_list(struct pa_module
*m
) {
116 AudioObjectPropertyAddress property_address
;
118 UInt32 i
, size
, num_devices
;
119 AudioDeviceID
*device_id
;
120 struct ca_device
*dev
;
124 pa_assert_se(u
= m
->userdata
);
126 property_address
.mSelector
= kAudioHardwarePropertyDevices
;
127 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
128 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
130 /* get the number of currently available audio devices */
131 err
= AudioObjectGetPropertyDataSize(kAudioObjectSystemObject
, &property_address
, 0, NULL
, &size
);
133 pa_log("Unable to get data size for kAudioHardwarePropertyDevices.");
137 num_devices
= size
/ sizeof(AudioDeviceID
);
138 device_id
= pa_xnew(AudioDeviceID
, num_devices
);
140 err
= AudioObjectGetPropertyData(kAudioObjectSystemObject
, &property_address
, 0, NULL
, &size
, device_id
);
142 pa_log("Unable to get kAudioHardwarePropertyDevices.");
147 /* scan for devices which are reported but not in our cached list */
148 for (i
= 0; i
< num_devices
; i
++) {
151 PA_LLIST_FOREACH(dev
, u
->devices
)
152 if (dev
->id
== device_id
[i
]) {
158 ca_device_added(m
, device_id
[i
]);
161 /* scan for devices which are in our cached list but are not reported */
164 PA_LLIST_FOREACH(dev
, u
->devices
) {
167 for (i
= 0; i
< num_devices
; i
++)
168 if (dev
->id
== device_id
[i
]) {
174 pa_log_debug("object id %d has been removed (module index %d) %p", (unsigned int) dev
->id
, dev
->module_index
, dev
);
175 pa_module_unload_request_by_index(m
->core
, dev
->module_index
, TRUE
);
176 PA_LLIST_REMOVE(ca_device
, u
->devices
, dev
);
178 /* the current list item pointer is not valid anymore, so start over. */
187 static OSStatus
property_listener_proc(AudioObjectID objectID
, UInt32 numberAddresses
,
188 const AudioObjectPropertyAddress inAddresses
[],
191 struct userdata
*u
= clientData
;
196 /* dispatch module load/unload operations in main thread */
197 write(u
->detect_fds
[1], &dummy
, 1);
202 static void detect_handle(pa_mainloop_api
*a
, pa_io_event
*e
, int fd
, pa_io_event_flags_t events
, void *userdata
) {
203 pa_module
*m
= userdata
;
209 ca_update_device_list(m
);
212 int pa__init(pa_module
*m
) {
213 struct userdata
*u
= pa_xnew0(struct userdata
, 1);
214 AudioObjectPropertyAddress property_address
;
221 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
222 pa_log("Failed to parse module arguments.");
226 pa_modargs_get_value_u32(ma
, "ioproc_frames", &u
->ioproc_frames
);
228 property_address
.mSelector
= kAudioHardwarePropertyDevices
;
229 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
230 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
232 if (AudioObjectAddPropertyListener(kAudioObjectSystemObject
, &property_address
, property_listener_proc
, u
)) {
233 pa_log("AudioObjectAddPropertyListener() failed.");
237 if (ca_update_device_list(m
))
240 pa_assert_se(pipe(u
->detect_fds
) == 0);
241 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
));
250 void pa__done(pa_module
*m
) {
252 struct ca_device
*dev
;
253 AudioObjectPropertyAddress property_address
;
256 pa_assert_se(u
= m
->userdata
);
260 property_address
.mSelector
= kAudioHardwarePropertyDevices
;
261 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
262 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
264 AudioObjectRemovePropertyListener(kAudioObjectSystemObject
, &property_address
, property_listener_proc
, u
);
267 struct ca_device
*next
= dev
->next
;
269 pa_module_unload_request_by_index(m
->core
, dev
->module_index
, TRUE
);
275 if (u
->detect_fds
[0] >= 0)
276 close(u
->detect_fds
[0]);
278 if (u
->detect_fds
[1] >= 0)
279 close(u
->detect_fds
[1]);
282 m
->core
->mainloop
->io_free(u
->detect_io
);