/* TODO:
- implement hardware volume controls
- handle audio device stream format changes (will require changes to the core)
+ - add an "off" mode that removes all sinks and sources
*/
#ifdef HAVE_CONFIG_H
#endif
#include <pulse/xmalloc.h>
-#include <pulse/util.h>
-#include <pulsecore/core-error.h>
#include <pulsecore/sink.h>
#include <pulsecore/source.h>
#include <pulsecore/module.h>
#include <pulsecore/strbuf.h>
#include <pulsecore/thread.h>
#include <pulsecore/thread-mq.h>
+#include <pulsecore/i18n.h>
#include <CoreAudio/CoreAudio.h>
#include <CoreAudio/CoreAudioTypes.h>
PA_MODULE_AUTHOR("Daniel Mack");
PA_MODULE_DESCRIPTION("CoreAudio device");
PA_MODULE_VERSION(PACKAGE_VERSION);
-PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_LOAD_ONCE(false);
PA_MODULE_USAGE("object_id=<the CoreAudio device id> "
"ioproc_frames=<audio frames per IOProc call> ");
pa_module *module;
pa_card *card;
- pa_bool_t running;
+ bool running;
char *device_name, *vendor_name;
char *name;
unsigned int channel_idx;
- pa_bool_t active;
+ bool active;
pa_channel_map map;
pa_sample_spec ss;
char *name;
unsigned int channel_idx;
- pa_bool_t active;
+ bool active;
pa_channel_map map;
pa_sample_spec ss;
PA_LLIST_FIELDS(coreaudio_source);
};
+static int card_set_profile(pa_card *c, pa_card_profile *new_profile) {
+ return 0;
+}
+
static OSStatus io_render_proc (AudioDeviceID device,
const AudioTimeStamp *now,
const AudioBufferList *inputData,
const AudioTimeStamp *inputTime,
AudioBufferList *outputData,
const AudioTimeStamp *outputTime,
- void *clientData)
-{
+ void *clientData) {
struct userdata *u = clientData;
pa_assert(u);
static OSStatus ca_stream_format_changed(AudioObjectID objectID,
UInt32 numberAddresses,
const AudioObjectPropertyAddress addresses[],
- void *clientData)
-{
+ void *clientData) {
struct userdata *u = clientData;
UInt32 i;
struct userdata *u;
pa_sample_spec *ss;
bool is_source;
- UInt32 v, total = 0;
+ UInt32 v = 0, total = 0;
UInt32 err, size = sizeof(v);
AudioObjectPropertyAddress property_address;
AudioObjectID stream_id;
u = sink->userdata;
ss = &sink->ss;
- is_source = FALSE;
+ is_source = false;
} else if (pa_source_isinstance(o)) {
coreaudio_source *source = PA_SOURCE(o)->userdata;
u = source->userdata;
ss = &source->ss;
- is_source = TRUE;
+ is_source = true;
} else
pa_assert_not_reached();
pa_assert(u);
- property_address.mScope = kAudioObjectPropertyScopeGlobal;
+ property_address.mScope = is_source ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput;
property_address.mElement = kAudioObjectPropertyElementMaster;
/* get the device latency */
property_address.mSelector = kAudioDevicePropertyLatency;
- size = sizeof(total);
- AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &total);
- total += v;
+ size = sizeof(v);
+ err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &v);
+ if (!err)
+ total += v;
+ else
+ pa_log_warn("Failed to get device latency: %i", err);
- /* the the IOProc buffer size */
+ /* the IOProc buffer size */
property_address.mSelector = kAudioDevicePropertyBufferFrameSize;
size = sizeof(v);
- AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &v);
- total += v;
+ err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &v);
+ if (!err)
+ total += v;
+ else
+ pa_log_warn("Failed to get buffer frame size: %i", err);
/* IOProc safety offset - this value is the same for both directions, hence we divide it by 2 */
property_address.mSelector = kAudioDevicePropertySafetyOffset;
size = sizeof(v);
- AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &v);
- total += v / 2;
+ err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &v);
+ if (!err)
+ total += v / 2;
+ else
+ pa_log_warn("Failed to get safety offset: %i", err);
/* get the stream latency.
* FIXME: this assumes the stream latency is the same for all streams */
err = AudioObjectGetPropertyData(u->object_id, &property_address, 0, NULL, &size, &stream_id);
if (!err) {
property_address.mSelector = kAudioStreamPropertyLatency;
+ property_address.mScope = kAudioObjectPropertyScopeGlobal;
size = sizeof(v);
err = AudioObjectGetPropertyData(stream_id, &property_address, 0, NULL, &size, &v);
if (!err)
total += v;
- }
+ else
+ pa_log_warn("Failed to get stream latency: %i", err);
+ } else
+ pa_log_warn("Failed to get streams: %i", err);
return pa_bytes_to_usec(total * pa_frame_size(ss), ss);
}
static void ca_device_check_device_state(struct userdata *u) {
coreaudio_sink *ca_sink;
coreaudio_source *ca_source;
- pa_bool_t active = FALSE;
+ bool active = false;
pa_assert(u);
for (ca_sink = u->sinks; ca_sink; ca_sink = ca_sink->next)
if (ca_sink->active)
- active = TRUE;
+ active = true;
for (ca_source = u->sources; ca_source; ca_source = ca_source->next)
if (ca_source->active)
- active = TRUE;
+ active = true;
if (active && !u->running)
AudioDeviceStart(u->object_id, u->proc_id);
pa_assert(sink);
if (PA_SINK_IS_OPENED(sink->pa_sink->thread_info.state)) {
- if (sink->pa_sink->thread_info.rewind_requested)
- pa_sink_process_rewind(sink->pa_sink, 0);
-
- audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, FALSE);
+ audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, false);
audio_chunk.length = buf->mDataByteSize;
audio_chunk.index = 0;
pa_assert(source);
if (PA_SOURCE_IS_OPENED(source->pa_source->thread_info.state)) {
- audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, TRUE);
+ audio_chunk.memblock = pa_memblock_new_fixed(u->module->core->mempool, buf->mData, buf->mDataByteSize, true);
audio_chunk.length = buf->mDataByteSize;
audio_chunk.index = 0;
return pa_source_process_msg(o, code, data, offset, chunk);;
}
-static int ca_sink_set_state(pa_sink *s, pa_sink_state_t state)
-{
+static int ca_sink_set_state(pa_sink *s, pa_sink_state_t state) {
coreaudio_sink *sink = s->userdata;
switch (state) {
case PA_SINK_SUSPENDED:
case PA_SINK_IDLE:
- sink->active = FALSE;
+ sink->active = false;
break;
case PA_SINK_RUNNING:
- sink->active = TRUE;
+ sink->active = true;
break;
case PA_SINK_UNLINKED:
new_data.card = u->card;
new_data.driver = __FILE__;
new_data.module = u->module;
- new_data.namereg_fail = FALSE;
+ new_data.namereg_fail = false;
pa_sink_new_data_set_name(&new_data, ca_sink->name);
pa_sink_new_data_set_channel_map(&new_data, &ca_sink->map);
pa_sink_new_data_set_sample_spec(&new_data, &ca_sink->ss);
return 0;
}
-static int ca_source_set_state(pa_source *s, pa_source_state_t state)
-{
+static int ca_source_set_state(pa_source *s, pa_source_state_t state) {
coreaudio_source *source = s->userdata;
switch (state) {
case PA_SOURCE_SUSPENDED:
case PA_SOURCE_IDLE:
- source->active = FALSE;
+ source->active = false;
break;
case PA_SOURCE_RUNNING:
- source->active = TRUE;
+ source->active = true;
break;
case PA_SOURCE_UNLINKED:
new_data.card = u->card;
new_data.driver = __FILE__;
new_data.module = u->module;
- new_data.namereg_fail = FALSE;
+ new_data.namereg_fail = false;
pa_source_new_data_set_name(&new_data, ca_source->name);
pa_source_new_data_set_channel_map(&new_data, &ca_source->map);
pa_source_new_data_set_sample_spec(&new_data, &ca_source->ss);
pa_thread_mq_install(&u->thread_mq);
for (;;) {
+ coreaudio_sink *ca_sink;
int ret;
- ret = pa_rtpoll_run(u->rtpoll, TRUE);
+ PA_LLIST_FOREACH(ca_sink, u->sinks) {
+ if (PA_UNLIKELY(ca_sink->pa_sink->thread_info.rewind_requested))
+ pa_sink_process_rewind(ca_sink->pa_sink, 0);
+ }
+
+ ret = pa_rtpoll_run(u->rtpoll, true);
if (ret < 0)
goto fail;
pa_modargs *ma = NULL;
char tmp[64];
pa_card_new_data card_new_data;
+ pa_card_profile *p;
coreaudio_sink *ca_sink;
coreaudio_source *ca_source;
AudioObjectPropertyAddress property_address;
if (!err)
u->vendor_name = pa_xstrdup(tmp);
+ /* add on profile */
+ p = pa_card_profile_new("on", _("On"), 0);
+ pa_hashmap_put(card_new_data.profiles, p->name, p);
+
/* create the card object */
u->card = pa_card_new(m->core, &card_new_data);
if (!u->card) {
pa_card_new_data_done(&card_new_data);
u->card->userdata = u;
+ u->card->set_profile = card_set_profile;
u->rtpoll = pa_rtpoll_new();
pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
PA_LLIST_HEAD_INIT(coreaudio_sink, u->sinks);
/* create sinks */
- ca_device_create_streams(m, FALSE);
+ ca_device_create_streams(m, false);
/* create sources */
- ca_device_create_streams(m, TRUE);
+ ca_device_create_streams(m, true);
/* create the message thread */
if (!(u->thread = pa_thread_new(u->device_name, thread_func, u))) {