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
23 - implement hardware volume controls
24 - handle audio device stream format changes (will require changes to the core)
25 - add an "off" mode that removes all sinks and sources
32 #include <pulse/xmalloc.h>
34 #include <pulsecore/sink.h>
35 #include <pulsecore/source.h>
36 #include <pulsecore/module.h>
37 #include <pulsecore/sample-util.h>
38 #include <pulsecore/core-util.h>
39 #include <pulsecore/modargs.h>
40 #include <pulsecore/log.h>
41 #include <pulsecore/macro.h>
42 #include <pulsecore/llist.h>
43 #include <pulsecore/card.h>
44 #include <pulsecore/strbuf.h>
45 #include <pulsecore/thread.h>
46 #include <pulsecore/thread-mq.h>
47 #include <pulsecore/i18n.h>
49 #include <CoreAudio/CoreAudio.h>
50 #include <CoreAudio/CoreAudioTypes.h>
51 #include <CoreAudio/AudioHardware.h>
53 #include "module-coreaudio-device-symdef.h"
55 #define DEFAULT_FRAMES_PER_IOPROC 512
57 PA_MODULE_AUTHOR("Daniel Mack");
58 PA_MODULE_DESCRIPTION("CoreAudio device");
59 PA_MODULE_VERSION(PACKAGE_VERSION
);
60 PA_MODULE_LOAD_ONCE(false);
61 PA_MODULE_USAGE("object_id=<the CoreAudio device id> "
62 "ioproc_frames=<audio frames per IOProc call> ");
64 static const char* const valid_modargs
[] = {
71 CA_MESSAGE_RENDER
= PA_SINK_MESSAGE_MAX
,
74 typedef struct coreaudio_sink coreaudio_sink
;
75 typedef struct coreaudio_source coreaudio_source
;
78 AudioObjectID object_id
;
79 AudioDeviceIOProcID proc_id
;
81 pa_thread_mq thread_mq
;
82 pa_asyncmsgq
*async_msgq
;
91 char *device_name
, *vendor_name
;
93 const AudioBufferList
*render_input_data
;
94 AudioBufferList
*render_output_data
;
96 AudioStreamBasicDescription stream_description
;
98 PA_LLIST_HEAD(coreaudio_sink
, sinks
);
99 PA_LLIST_HEAD(coreaudio_source
, sources
);
102 struct coreaudio_sink
{
104 struct userdata
*userdata
;
107 unsigned int channel_idx
;
113 PA_LLIST_FIELDS(coreaudio_sink
);
116 struct coreaudio_source
{
117 pa_source
*pa_source
;
118 struct userdata
*userdata
;
121 unsigned int channel_idx
;
127 PA_LLIST_FIELDS(coreaudio_source
);
130 static int card_set_profile(pa_card
*c
, pa_card_profile
*new_profile
) {
134 static OSStatus
io_render_proc (AudioDeviceID device
,
135 const AudioTimeStamp
*now
,
136 const AudioBufferList
*inputData
,
137 const AudioTimeStamp
*inputTime
,
138 AudioBufferList
*outputData
,
139 const AudioTimeStamp
*outputTime
,
141 struct userdata
*u
= clientData
;
144 pa_assert(device
== u
->object_id
);
146 u
->render_input_data
= inputData
;
147 u
->render_output_data
= outputData
;
150 pa_assert_se(pa_asyncmsgq_send(u
->async_msgq
, PA_MSGOBJECT(u
->sinks
->pa_sink
),
151 CA_MESSAGE_RENDER
, NULL
, 0, NULL
) == 0);
154 pa_assert_se(pa_asyncmsgq_send(u
->async_msgq
, PA_MSGOBJECT(u
->sources
->pa_source
),
155 CA_MESSAGE_RENDER
, NULL
, 0, NULL
) == 0);
160 static OSStatus
ca_stream_format_changed(AudioObjectID objectID
,
161 UInt32 numberAddresses
,
162 const AudioObjectPropertyAddress addresses
[],
164 struct userdata
*u
= clientData
;
169 /* REVISIT: PA can't currently handle external format change requests.
170 * Hence, we set the original format back in this callback to avoid horrible audio artefacts.
171 * The device settings will appear to be 'locked' for any application as long as the PA daemon is running.
172 * Once we're able to propagate such events up in the core, this needs to be changed. */
174 for (i
= 0; i
< numberAddresses
; i
++)
175 AudioObjectSetPropertyData(objectID
, addresses
+ i
, 0, NULL
, sizeof(u
->stream_description
), &u
->stream_description
);
180 static pa_usec_t
get_latency_us(pa_object
*o
) {
184 UInt32 v
= 0, total
= 0;
185 UInt32 err
, size
= sizeof(v
);
186 AudioObjectPropertyAddress property_address
;
187 AudioObjectID stream_id
;
189 if (pa_sink_isinstance(o
)) {
190 coreaudio_sink
*sink
= PA_SINK(o
)->userdata
;
195 } else if (pa_source_isinstance(o
)) {
196 coreaudio_source
*source
= PA_SOURCE(o
)->userdata
;
198 u
= source
->userdata
;
202 pa_assert_not_reached();
206 property_address
.mScope
= is_source
? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
207 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
209 /* get the device latency */
210 property_address
.mSelector
= kAudioDevicePropertyLatency
;
212 err
= AudioObjectGetPropertyData(u
->object_id
, &property_address
, 0, NULL
, &size
, &v
);
216 pa_log_warn("Failed to get device latency: %i", err
);
218 /* the IOProc buffer size */
219 property_address
.mSelector
= kAudioDevicePropertyBufferFrameSize
;
221 err
= AudioObjectGetPropertyData(u
->object_id
, &property_address
, 0, NULL
, &size
, &v
);
225 pa_log_warn("Failed to get buffer frame size: %i", err
);
227 /* IOProc safety offset - this value is the same for both directions, hence we divide it by 2 */
228 property_address
.mSelector
= kAudioDevicePropertySafetyOffset
;
230 err
= AudioObjectGetPropertyData(u
->object_id
, &property_address
, 0, NULL
, &size
, &v
);
234 pa_log_warn("Failed to get safety offset: %i", err
);
236 /* get the stream latency.
237 * FIXME: this assumes the stream latency is the same for all streams */
238 property_address
.mSelector
= kAudioDevicePropertyStreams
;
239 size
= sizeof(stream_id
);
240 err
= AudioObjectGetPropertyData(u
->object_id
, &property_address
, 0, NULL
, &size
, &stream_id
);
242 property_address
.mSelector
= kAudioStreamPropertyLatency
;
243 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
245 err
= AudioObjectGetPropertyData(stream_id
, &property_address
, 0, NULL
, &size
, &v
);
249 pa_log_warn("Failed to get stream latency: %i", err
);
251 pa_log_warn("Failed to get streams: %i", err
);
253 return pa_bytes_to_usec(total
* pa_frame_size(ss
), ss
);
256 static void ca_device_check_device_state(struct userdata
*u
) {
257 coreaudio_sink
*ca_sink
;
258 coreaudio_source
*ca_source
;
263 for (ca_sink
= u
->sinks
; ca_sink
; ca_sink
= ca_sink
->next
)
267 for (ca_source
= u
->sources
; ca_source
; ca_source
= ca_source
->next
)
268 if (ca_source
->active
)
271 if (active
&& !u
->running
)
272 AudioDeviceStart(u
->object_id
, u
->proc_id
);
273 else if (!active
&& u
->running
)
274 AudioDeviceStop(u
->object_id
, u
->proc_id
);
279 static int sink_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
280 coreaudio_sink
*sink
= PA_SINK(o
)->userdata
;
281 struct userdata
*u
= sink
->userdata
;
283 pa_memchunk audio_chunk
;
286 case CA_MESSAGE_RENDER
: {
288 for (i
= 0; i
< u
->render_output_data
->mNumberBuffers
; i
++) {
289 AudioBuffer
*buf
= u
->render_output_data
->mBuffers
+ i
;
293 if (PA_SINK_IS_OPENED(sink
->pa_sink
->thread_info
.state
)) {
294 audio_chunk
.memblock
= pa_memblock_new_fixed(u
->module
->core
->mempool
, buf
->mData
, buf
->mDataByteSize
, false);
295 audio_chunk
.length
= buf
->mDataByteSize
;
296 audio_chunk
.index
= 0;
298 pa_sink_render_into_full(sink
->pa_sink
, &audio_chunk
);
299 pa_memblock_unref_fixed(audio_chunk
.memblock
);
308 case PA_SINK_MESSAGE_GET_LATENCY
: {
309 *((pa_usec_t
*) data
) = get_latency_us(PA_OBJECT(o
));
314 return pa_sink_process_msg(o
, code
, data
, offset
, chunk
);
317 static int source_process_msg(pa_msgobject
*o
, int code
, void *data
, int64_t offset
, pa_memchunk
*chunk
) {
318 coreaudio_source
*source
= PA_SOURCE(o
)->userdata
;
319 struct userdata
*u
= source
->userdata
;
321 pa_memchunk audio_chunk
;
324 case CA_MESSAGE_RENDER
: {
326 for (i
= 0; i
< u
->render_input_data
->mNumberBuffers
; i
++) {
327 const AudioBuffer
*buf
= u
->render_input_data
->mBuffers
+ i
;
331 if (PA_SOURCE_IS_OPENED(source
->pa_source
->thread_info
.state
)) {
332 audio_chunk
.memblock
= pa_memblock_new_fixed(u
->module
->core
->mempool
, buf
->mData
, buf
->mDataByteSize
, true);
333 audio_chunk
.length
= buf
->mDataByteSize
;
334 audio_chunk
.index
= 0;
336 pa_source_post(source
->pa_source
, &audio_chunk
);
337 pa_memblock_unref_fixed(audio_chunk
.memblock
);
340 source
= source
->next
;
346 case PA_SOURCE_MESSAGE_GET_LATENCY
: {
347 *((pa_usec_t
*) data
) = get_latency_us(PA_OBJECT(o
));
352 return pa_source_process_msg(o
, code
, data
, offset
, chunk
);;
355 static int ca_sink_set_state(pa_sink
*s
, pa_sink_state_t state
) {
356 coreaudio_sink
*sink
= s
->userdata
;
359 case PA_SINK_SUSPENDED
:
361 sink
->active
= false;
364 case PA_SINK_RUNNING
:
368 case PA_SINK_UNLINKED
:
370 case PA_SINK_INVALID_STATE
:
374 ca_device_check_device_state(sink
->userdata
);
379 static int ca_device_create_sink(pa_module
*m
, AudioBuffer
*buf
, int channel_idx
) {
382 struct userdata
*u
= m
->userdata
;
383 pa_sink_new_data new_data
;
384 pa_sink_flags_t flags
= PA_SINK_LATENCY
| PA_SINK_HARDWARE
;
385 coreaudio_sink
*ca_sink
;
390 AudioObjectPropertyAddress property_address
;
392 ca_sink
= pa_xnew0(coreaudio_sink
, 1);
393 ca_sink
->map
.channels
= buf
->mNumberChannels
;
394 ca_sink
->ss
.channels
= buf
->mNumberChannels
;
395 ca_sink
->channel_idx
= channel_idx
;
397 /* build a name for this stream */
398 strbuf
= pa_strbuf_new();
400 for (i
= 0; i
< buf
->mNumberChannels
; i
++) {
401 property_address
.mSelector
= kAudioObjectPropertyElementName
;
402 property_address
.mScope
= kAudioDevicePropertyScopeOutput
;
403 property_address
.mElement
= channel_idx
+ i
+ 1;
405 err
= AudioObjectGetPropertyData(u
->object_id
, &property_address
, 0, NULL
, &size
, tmp
);
406 if (err
|| !strlen(tmp
))
407 snprintf(tmp
, sizeof(tmp
), "Channel %d", (int) property_address
.mElement
);
410 pa_strbuf_puts(strbuf
, ", ");
412 pa_strbuf_puts(strbuf
, tmp
);
415 ca_sink
->name
= pa_strbuf_tostring_free(strbuf
);
417 pa_log_debug("Stream name is >%s<", ca_sink
->name
);
419 /* default to mono streams */
420 for (i
= 0; i
< ca_sink
->map
.channels
; i
++)
421 ca_sink
->map
.map
[i
] = PA_CHANNEL_POSITION_MONO
;
423 if (buf
->mNumberChannels
== 2) {
424 ca_sink
->map
.map
[0] = PA_CHANNEL_POSITION_LEFT
;
425 ca_sink
->map
.map
[1] = PA_CHANNEL_POSITION_RIGHT
;
428 ca_sink
->ss
.rate
= u
->stream_description
.mSampleRate
;
429 ca_sink
->ss
.format
= PA_SAMPLE_FLOAT32LE
;
431 pa_sink_new_data_init(&new_data
);
432 new_data
.card
= u
->card
;
433 new_data
.driver
= __FILE__
;
434 new_data
.module
= u
->module
;
435 new_data
.namereg_fail
= false;
436 pa_sink_new_data_set_name(&new_data
, ca_sink
->name
);
437 pa_sink_new_data_set_channel_map(&new_data
, &ca_sink
->map
);
438 pa_sink_new_data_set_sample_spec(&new_data
, &ca_sink
->ss
);
439 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_STRING
, u
->device_name
);
440 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_PRODUCT_NAME
, u
->device_name
);
441 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, u
->device_name
);
442 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_ACCESS_MODE
, "mmap");
443 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_CLASS
, "sound");
444 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_API
, "CoreAudio");
445 pa_proplist_setf(new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE
, "%lu", (unsigned long) buf
->mDataByteSize
);
448 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_VENDOR_NAME
, u
->vendor_name
);
450 sink
= pa_sink_new(m
->core
, &new_data
, flags
);
451 pa_sink_new_data_done(&new_data
);
454 pa_log("unable to create sink.");
458 sink
->parent
.process_msg
= sink_process_msg
;
459 sink
->userdata
= ca_sink
;
460 sink
->set_state
= ca_sink_set_state
;
462 pa_sink_set_asyncmsgq(sink
, u
->thread_mq
.inq
);
463 pa_sink_set_rtpoll(sink
, u
->rtpoll
);
465 ca_sink
->pa_sink
= sink
;
466 ca_sink
->userdata
= u
;
468 PA_LLIST_PREPEND(coreaudio_sink
, u
->sinks
, ca_sink
);
473 static int ca_source_set_state(pa_source
*s
, pa_source_state_t state
) {
474 coreaudio_source
*source
= s
->userdata
;
477 case PA_SOURCE_SUSPENDED
:
479 source
->active
= false;
482 case PA_SOURCE_RUNNING
:
483 source
->active
= true;
486 case PA_SOURCE_UNLINKED
:
488 case PA_SOURCE_INVALID_STATE
:
492 ca_device_check_device_state(source
->userdata
);
497 static int ca_device_create_source(pa_module
*m
, AudioBuffer
*buf
, int channel_idx
) {
500 struct userdata
*u
= m
->userdata
;
501 pa_source_new_data new_data
;
502 pa_source_flags_t flags
= PA_SOURCE_LATENCY
| PA_SOURCE_HARDWARE
;
503 coreaudio_source
*ca_source
;
508 AudioObjectPropertyAddress property_address
;
510 ca_source
= pa_xnew0(coreaudio_source
, 1);
511 ca_source
->map
.channels
= buf
->mNumberChannels
;
512 ca_source
->ss
.channels
= buf
->mNumberChannels
;
513 ca_source
->channel_idx
= channel_idx
;
515 /* build a name for this stream */
516 strbuf
= pa_strbuf_new();
518 for (i
= 0; i
< buf
->mNumberChannels
; i
++) {
519 property_address
.mSelector
= kAudioObjectPropertyElementName
;
520 property_address
.mScope
= kAudioDevicePropertyScopeInput
;
521 property_address
.mElement
= channel_idx
+ i
+ 1;
523 err
= AudioObjectGetPropertyData(u
->object_id
, &property_address
, 0, NULL
, &size
, tmp
);
524 if (err
|| !strlen(tmp
))
525 snprintf(tmp
, sizeof(tmp
), "Channel %d", (int) property_address
.mElement
);
528 pa_strbuf_puts(strbuf
, ", ");
530 pa_strbuf_puts(strbuf
, tmp
);
533 ca_source
->name
= pa_strbuf_tostring_free(strbuf
);
535 pa_log_debug("Stream name is >%s<", ca_source
->name
);
537 /* default to mono streams */
538 for (i
= 0; i
< ca_source
->map
.channels
; i
++)
539 ca_source
->map
.map
[i
] = PA_CHANNEL_POSITION_MONO
;
541 if (buf
->mNumberChannels
== 2) {
542 ca_source
->map
.map
[0] = PA_CHANNEL_POSITION_LEFT
;
543 ca_source
->map
.map
[1] = PA_CHANNEL_POSITION_RIGHT
;
546 ca_source
->ss
.rate
= u
->stream_description
.mSampleRate
;
547 ca_source
->ss
.format
= PA_SAMPLE_FLOAT32LE
;
549 pa_source_new_data_init(&new_data
);
550 new_data
.card
= u
->card
;
551 new_data
.driver
= __FILE__
;
552 new_data
.module
= u
->module
;
553 new_data
.namereg_fail
= false;
554 pa_source_new_data_set_name(&new_data
, ca_source
->name
);
555 pa_source_new_data_set_channel_map(&new_data
, &ca_source
->map
);
556 pa_source_new_data_set_sample_spec(&new_data
, &ca_source
->ss
);
557 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_STRING
, u
->device_name
);
558 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_PRODUCT_NAME
, u
->device_name
);
559 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_DESCRIPTION
, u
->device_name
);
560 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_ACCESS_MODE
, "mmap");
561 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_CLASS
, "sound");
562 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_API
, "CoreAudio");
563 pa_proplist_setf(new_data
.proplist
, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE
, "%lu", (unsigned long) buf
->mDataByteSize
);
566 pa_proplist_sets(new_data
.proplist
, PA_PROP_DEVICE_VENDOR_NAME
, u
->vendor_name
);
568 source
= pa_source_new(m
->core
, &new_data
, flags
);
569 pa_source_new_data_done(&new_data
);
572 pa_log("unable to create source.");
576 source
->parent
.process_msg
= source_process_msg
;
577 source
->userdata
= ca_source
;
578 source
->set_state
= ca_source_set_state
;
580 pa_source_set_asyncmsgq(source
, u
->thread_mq
.inq
);
581 pa_source_set_rtpoll(source
, u
->rtpoll
);
583 ca_source
->pa_source
= source
;
584 ca_source
->userdata
= u
;
586 PA_LLIST_PREPEND(coreaudio_source
, u
->sources
, ca_source
);
591 static int ca_device_create_streams(pa_module
*m
, bool direction_in
) {
593 UInt32 size
, i
, channel_idx
;
594 struct userdata
*u
= m
->userdata
;
595 AudioBufferList
*buffer_list
;
596 AudioObjectPropertyAddress property_address
;
598 property_address
.mScope
= direction_in
? kAudioDevicePropertyScopeInput
: kAudioDevicePropertyScopeOutput
;
599 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
601 /* get current stream format */
602 size
= sizeof(AudioStreamBasicDescription
);
603 property_address
.mSelector
= kAudioDevicePropertyStreamFormat
;
604 err
= AudioObjectGetPropertyData(u
->object_id
, &property_address
, 0, NULL
, &size
, &u
->stream_description
);
606 /* no appropriate streams found - silently bail. */
610 if (u
->stream_description
.mFormatID
!= kAudioFormatLinearPCM
) {
611 pa_log("Unsupported audio format '%c%c%c%c'",
612 (char) (u
->stream_description
.mFormatID
>> 24),
613 (char) (u
->stream_description
.mFormatID
>> 16) & 0xff,
614 (char) (u
->stream_description
.mFormatID
>> 8) & 0xff,
615 (char) (u
->stream_description
.mFormatID
& 0xff));
619 /* get stream configuration */
621 property_address
.mSelector
= kAudioDevicePropertyStreamConfiguration
;
622 err
= AudioObjectGetPropertyDataSize(u
->object_id
, &property_address
, 0, NULL
, &size
);
624 pa_log("Failed to get kAudioDevicePropertyStreamConfiguration (%s).", direction_in
? "input" : "output");
631 buffer_list
= (AudioBufferList
*) pa_xmalloc(size
);
632 err
= AudioObjectGetPropertyData(u
->object_id
, &property_address
, 0, NULL
, &size
, buffer_list
);
635 pa_log_debug("Sample rate: %f", u
->stream_description
.mSampleRate
);
636 pa_log_debug("%d bytes per packet", (unsigned int) u
->stream_description
.mBytesPerPacket
);
637 pa_log_debug("%d frames per packet", (unsigned int) u
->stream_description
.mFramesPerPacket
);
638 pa_log_debug("%d bytes per frame", (unsigned int) u
->stream_description
.mBytesPerFrame
);
639 pa_log_debug("%d channels per frame", (unsigned int) u
->stream_description
.mChannelsPerFrame
);
640 pa_log_debug("%d bits per channel", (unsigned int) u
->stream_description
.mBitsPerChannel
);
642 for (channel_idx
= 0, i
= 0; i
< buffer_list
->mNumberBuffers
; i
++) {
643 AudioBuffer
*buf
= buffer_list
->mBuffers
+ i
;
646 ca_device_create_source(m
, buf
, channel_idx
);
648 ca_device_create_sink(m
, buf
, channel_idx
);
650 channel_idx
+= buf
->mNumberChannels
;
654 pa_xfree(buffer_list
);
658 static void thread_func(void *userdata
) {
659 struct userdata
*u
= userdata
;
662 pa_assert(u
->module
);
663 pa_assert(u
->module
->core
);
665 pa_log_debug("Thread starting up");
667 if (u
->module
->core
->realtime_scheduling
)
668 pa_make_realtime(u
->module
->core
->realtime_priority
);
670 pa_thread_mq_install(&u
->thread_mq
);
673 coreaudio_sink
*ca_sink
;
676 PA_LLIST_FOREACH(ca_sink
, u
->sinks
) {
677 if (PA_UNLIKELY(ca_sink
->pa_sink
->thread_info
.rewind_requested
))
678 pa_sink_process_rewind(ca_sink
->pa_sink
, 0);
681 ret
= pa_rtpoll_run(u
->rtpoll
, true);
691 /* If this was no regular exit from the loop we have to continue
692 * processing messages until we received PA_MESSAGE_SHUTDOWN */
693 pa_asyncmsgq_post(u
->thread_mq
.outq
, PA_MSGOBJECT(u
->module
->core
), PA_CORE_MESSAGE_UNLOAD_MODULE
, u
->module
, 0, NULL
, NULL
);
694 pa_asyncmsgq_wait_for(u
->thread_mq
.inq
, PA_MESSAGE_SHUTDOWN
);
697 pa_log_debug("Thread shutting down");
700 int pa__init(pa_module
*m
) {
703 struct userdata
*u
= NULL
;
704 pa_modargs
*ma
= NULL
;
706 pa_card_new_data card_new_data
;
708 coreaudio_sink
*ca_sink
;
709 coreaudio_source
*ca_source
;
710 AudioObjectPropertyAddress property_address
;
714 if (!(ma
= pa_modargs_new(m
->argument
, valid_modargs
))) {
715 pa_log("Failed to parse module arguments.");
719 u
= pa_xnew0(struct userdata
, 1);
723 if (pa_modargs_get_value_u32(ma
, "object_id", (unsigned int *) &u
->object_id
) != 0) {
724 pa_log("Failed to parse object_id argument.");
728 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
729 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
731 /* get device product name */
732 property_address
.mSelector
= kAudioDevicePropertyDeviceName
;
734 err
= AudioObjectGetPropertyData(u
->object_id
, &property_address
, 0, NULL
, &size
, tmp
);
736 pa_log("Failed to get kAudioDevicePropertyDeviceName (err = %08x).", (int) err
);
740 u
->device_name
= pa_xstrdup(tmp
);
742 pa_card_new_data_init(&card_new_data
);
743 pa_proplist_sets(card_new_data
.proplist
, PA_PROP_DEVICE_STRING
, tmp
);
744 card_new_data
.driver
= __FILE__
;
745 pa_card_new_data_set_name(&card_new_data
, tmp
);
746 pa_log_info("Initializing module for CoreAudio device '%s' (id %d)", tmp
, (unsigned int) u
->object_id
);
748 /* get device vendor name (may fail) */
749 property_address
.mSelector
= kAudioDevicePropertyDeviceManufacturer
;
751 err
= AudioObjectGetPropertyData(u
->object_id
, &property_address
, 0, NULL
, &size
, tmp
);
753 u
->vendor_name
= pa_xstrdup(tmp
);
756 p
= pa_card_profile_new("on", _("On"), 0);
757 pa_hashmap_put(card_new_data
.profiles
, p
->name
, p
);
759 /* create the card object */
760 u
->card
= pa_card_new(m
->core
, &card_new_data
);
762 pa_log("Unable to create card.\n");
766 pa_card_new_data_done(&card_new_data
);
767 u
->card
->userdata
= u
;
768 u
->card
->set_profile
= card_set_profile
;
770 u
->rtpoll
= pa_rtpoll_new();
771 pa_thread_mq_init(&u
->thread_mq
, m
->core
->mainloop
, u
->rtpoll
);
772 u
->async_msgq
= pa_asyncmsgq_new(0);
773 pa_rtpoll_item_new_asyncmsgq_read(u
->rtpoll
, PA_RTPOLL_EARLY
-1, u
->async_msgq
);
775 PA_LLIST_HEAD_INIT(coreaudio_sink
, u
->sinks
);
778 ca_device_create_streams(m
, false);
781 ca_device_create_streams(m
, true);
783 /* create the message thread */
784 if (!(u
->thread
= pa_thread_new(u
->device_name
, thread_func
, u
))) {
785 pa_log("Failed to create thread.");
789 /* register notification callback for stream format changes */
790 property_address
.mSelector
= kAudioDevicePropertyStreamFormat
;
791 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
792 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
794 AudioObjectAddPropertyListener(u
->object_id
, &property_address
, ca_stream_format_changed
, u
);
796 /* set number of frames in IOProc */
797 frames
= DEFAULT_FRAMES_PER_IOPROC
;
798 pa_modargs_get_value_u32(ma
, "ioproc_frames", (unsigned int *) &frames
);
800 property_address
.mSelector
= kAudioDevicePropertyBufferFrameSize
;
801 AudioObjectSetPropertyData(u
->object_id
, &property_address
, 0, NULL
, sizeof(frames
), &frames
);
802 pa_log_debug("%u frames per IOProc\n", (unsigned int) frames
);
804 /* create one ioproc for both directions */
805 err
= AudioDeviceCreateIOProcID(u
->object_id
, io_render_proc
, u
, &u
->proc_id
);
807 pa_log("AudioDeviceCreateIOProcID() failed (err = %08x\n).", (int) err
);
811 for (ca_sink
= u
->sinks
; ca_sink
; ca_sink
= ca_sink
->next
)
812 pa_sink_put(ca_sink
->pa_sink
);
814 for (ca_source
= u
->sources
; ca_source
; ca_source
= ca_source
->next
)
815 pa_source_put(ca_source
->pa_source
);
831 void pa__done(pa_module
*m
) {
833 coreaudio_sink
*ca_sink
;
834 coreaudio_source
*ca_source
;
835 AudioObjectPropertyAddress property_address
;
843 for (ca_sink
= u
->sinks
; ca_sink
; ca_sink
= ca_sink
->next
)
844 if (ca_sink
->pa_sink
)
845 pa_sink_unlink(ca_sink
->pa_sink
);
848 for (ca_source
= u
->sources
; ca_source
; ca_source
= ca_source
->next
)
849 if (ca_source
->pa_source
)
850 pa_source_unlink(ca_source
->pa_source
);
853 pa_asyncmsgq_send(u
->thread_mq
.inq
, NULL
, PA_MESSAGE_SHUTDOWN
, NULL
, 0, NULL
);
854 pa_thread_free(u
->thread
);
855 pa_thread_mq_done(&u
->thread_mq
);
856 pa_asyncmsgq_unref(u
->async_msgq
);
860 for (ca_sink
= u
->sinks
; ca_sink
;) {
861 coreaudio_sink
*next
= ca_sink
->next
;
863 if (ca_sink
->pa_sink
)
864 pa_sink_unref(ca_sink
->pa_sink
);
866 pa_xfree(ca_sink
->name
);
872 for (ca_source
= u
->sources
; ca_source
;) {
873 coreaudio_source
*next
= ca_source
->next
;
875 if (ca_source
->pa_source
)
876 pa_source_unref(ca_source
->pa_source
);
878 pa_xfree(ca_source
->name
);
884 AudioDeviceStop(u
->object_id
, u
->proc_id
);
885 AudioDeviceDestroyIOProcID(u
->object_id
, u
->proc_id
);
888 property_address
.mSelector
= kAudioDevicePropertyStreamFormat
;
889 property_address
.mScope
= kAudioObjectPropertyScopeGlobal
;
890 property_address
.mElement
= kAudioObjectPropertyElementMaster
;
892 AudioObjectRemovePropertyListener(kAudioObjectSystemObject
, &property_address
, ca_stream_format_changed
, u
);
894 pa_xfree(u
->device_name
);
895 pa_xfree(u
->vendor_name
);
896 pa_rtpoll_free(u
->rtpoll
);
897 pa_card_free(u
->card
);