static int sink_process_msg( pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { struct userdata *u = PA_SINK(o)->userdata; switch (code) { case PA_SINK_MESSAGE_SET_STATE: if (pa_sink_get_state(u->sink) == PA_SINK_SUSPENDED || pa_sink_get_state(u->sink) == PA_SINK_INIT) { if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING || PA_PTR_TO_UINT(data) == PA_SINK_IDLE) u->timestamp = pa_rtclock_now(); } break; case PA_SINK_MESSAGE_GET_LATENCY: { pa_usec_t now; now = pa_rtclock_now(); *((int64_t*) data) = (int64_t)u->timestamp - (int64_t)now; return 0; } } return pa_sink_process_msg(o, code, data, offset, chunk); }
/* Called from main thread */ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; char *output_description; const char *n; if (!dest) return; pa_sink_input_assert_ref(i); pa_assert_ctl_context(); pa_assert_se(u = i->userdata); output_description = pa_sprintf_malloc("Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION))); pa_source_output_set_property(u->source_output, PA_PROP_MEDIA_NAME, output_description); pa_xfree(output_description); if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME))) pa_source_output_set_property(u->source_output, PA_PROP_MEDIA_ICON_NAME, n); if (pa_sink_get_state(dest) == PA_SINK_SUSPENDED) pa_source_output_cork(u->source_output, true); else pa_source_output_cork(u->source_output, false); update_adjust_timer(u); }
static pa_hook_result_t device_state_changed_hook_cb(pa_core *c, pa_object *o, struct userdata *u) { struct device_info *d; pa_assert(c); pa_object_assert_ref(o); pa_assert(u); if (!(d = pa_hashmap_get(u->device_infos, o))) return PA_HOOK_OK; if (pa_sink_isinstance(o)) { pa_sink *s = PA_SINK(o); pa_sink_state_t state = pa_sink_get_state(s); if (pa_sink_check_suspend(s) <= 0) if (PA_SINK_IS_OPENED(state)) restart(d); } else if (pa_source_isinstance(o)) { pa_source *s = PA_SOURCE(o); pa_source_state_t state = pa_source_get_state(s); if (pa_source_check_suspend(s) <= 0) if (PA_SOURCE_IS_OPENED(state)) restart(d); } return PA_HOOK_OK; }
char *pa_sink_list_to_string(pa_core *c) { pa_strbuf *s; pa_sink *sink; uint32_t idx = PA_IDXSET_INVALID; static const char* const state_table[] = { [PA_SINK_RUNNING] = "RUNNING", [PA_SINK_SUSPENDED] = "SUSPENDED", [PA_SINK_IDLE] = "IDLE", [PA_SINK_UNLINKED] = "UNLINKED" }; pa_assert(c); s = pa_strbuf_new(); pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks)); for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) { char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; pa_strbuf_printf( s, " %c index: %u\n" "\tname: <%s>\n" "\tdriver: <%s>\n" "\tflags: %s%s%s%s\n" "\tstate: %s\n" "\tvolume: <%s>\n" "\tmute: <%i>\n" "\tlatency: <%0.0f usec>\n" "\tmonitor source: <%u>\n" "\tsample spec: <%s>\n" "\tchannel map: <%s>\n" "\tused by: <%u>\n" "\tlinked by: <%u>\n", c->default_sink_name && !strcmp(sink->name, c->default_sink_name) ? '*' : ' ', sink->index, sink->name, sink->driver, sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", sink->flags & PA_SINK_LATENCY ? "LATENCY " : "", sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "", sink->flags & PA_SINK_NETWORK ? "NETWORK " : "", state_table[pa_sink_get_state(sink)], pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink)), !!pa_sink_get_mute(sink), (double) pa_sink_get_latency(sink), sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX, pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map), pa_sink_used_by(sink), pa_sink_linked_by(sink)); if (sink->module) pa_strbuf_printf(s, "\tmodule: <%u>\n", sink->module->index); if (sink->description) pa_strbuf_printf(s, "\tdescription: <%s>\n", sink->description); } return pa_strbuf_tostring_free(s); }
/* Called from main thread */ static void sink_input_moving_cb(pa_sink_input *i, pa_sink *dest) { struct userdata *u; pa_proplist *p; const char *n; if (!dest) return; pa_sink_input_assert_ref(i); pa_assert_ctl_context(); pa_assert_se(u = i->userdata); p = pa_proplist_new(); pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION))); if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME))) pa_proplist_sets(p, PA_PROP_MEDIA_ICON_NAME, n); pa_source_output_update_proplist(u->source_output, PA_UPDATE_REPLACE, p); pa_proplist_free(p); if (pa_sink_get_state(dest) == PA_SINK_SUSPENDED) pa_source_output_cork(u->source_output, true); else pa_source_output_cork(u->source_output, false); update_adjust_timer(u); }
void pa_core_maybe_vacuum(pa_core *c) { pa_assert(c); if (pa_idxset_isempty(c->sink_inputs) && pa_idxset_isempty(c->source_outputs)) { pa_log_debug("Hmm, no streams around, trying to vacuum."); } else { pa_sink *si; pa_source *so; uint32_t idx; idx = 0; PA_IDXSET_FOREACH(si, c->sinks, idx) if (pa_sink_get_state(si) != PA_SINK_SUSPENDED) return; idx = 0; PA_IDXSET_FOREACH(so, c->sources, idx) if (pa_source_get_state(so) != PA_SOURCE_SUSPENDED) return; pa_log_info("All sinks and sources are suspended, vacuuming memory"); } pa_mempool_vacuum(c->mempool); if (c->rw_mempool) pa_mempool_vacuum(c->rw_mempool); }
/* Called from main context */ static void sink_set_mute_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) return; pa_sink_input_set_mute(u->sink_input, s->muted, s->save_muted); }
/* Called from main context */ static void sink_set_volume_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SINK_IS_LINKED(pa_sink_get_state(s)) || !PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->sink_input))) return; pa_sink_input_set_volume(u->sink_input, &s->real_volume, s->save_volume, TRUE); }
/* force_state can be -1 - do not force , 0 force inactive, 1 force active */ static int perform_activity_action(pa_sink *sink, struct pa_policy_activity_variable *var, int force_state) { struct pa_policy_context_rule *rule; union pa_policy_context_action *actn; int is_opened; pa_assert(sink); if ((force_state != -1 && force_state == 1) || (force_state == -1 && PA_SINK_IS_OPENED(pa_sink_get_state(sink)))) { rule = var->active_rules; is_opened = 1; } else { rule = var->inactive_rules; is_opened = 0; } for ( ; rule != NULL; rule = rule->next) { if (rule->match.method(sink->name, &rule->match.arg)) { if (force_state == -1 && var->sink_opened != -1 && var->sink_opened == is_opened) { pa_log_debug("Already executed actions for state change, skip."); return 1; } var->sink_opened = is_opened; for (actn = rule->actions; actn; actn = actn->any.next) { if (!perform_action(var->userdata, actn, NULL)) pa_log("Failed to perform activity action."); } } } return 1; }
int pa__init(pa_module *m) { pa_modargs *ma = NULL; struct userdata *u; pa_sink *sink = NULL; pa_sink_input_new_data sink_input_data; pa_bool_t sink_dont_move; pa_source *source = NULL; pa_source_output_new_data source_output_data; pa_bool_t source_dont_move; uint32_t latency_msec; pa_sample_spec ss; pa_channel_map map; bool format_set = false; bool rate_set = false; bool channels_set = false; pa_memchunk silence; uint32_t adjust_time_sec; const char *n; pa_bool_t remix = TRUE; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); goto fail; } n = pa_modargs_get_value(ma, "source", NULL); if (n && !(source = pa_namereg_get(m->core, n, PA_NAMEREG_SOURCE))) { pa_log("No such source."); goto fail; } n = pa_modargs_get_value(ma, "sink", NULL); if (n && !(sink = pa_namereg_get(m->core, n, PA_NAMEREG_SINK))) { pa_log("No such sink."); goto fail; } if (pa_modargs_get_value_boolean(ma, "remix", &remix) < 0) { pa_log("Invalid boolean remix parameter"); goto fail; } if (sink) { ss = sink->sample_spec; map = sink->channel_map; format_set = true; rate_set = true; channels_set = true; } else if (source) { ss = source->sample_spec; map = source->channel_map; format_set = true; rate_set = true; channels_set = true; } else { /* FIXME: Dummy stream format, needed because pa_sink_input_new() * requires valid sample spec and channel map even when all the FIX_* * stream flags are specified. pa_sink_input_new() should be changed * to ignore the sample spec and channel map when the FIX_* flags are * present. */ ss.format = PA_SAMPLE_U8; ss.rate = 8000; ss.channels = 1; map.channels = 1; map.map[0] = PA_CHANNEL_POSITION_MONO; } if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { pa_log("Invalid sample format specification or channel map"); goto fail; } if (pa_modargs_get_value(ma, "format", NULL)) format_set = true; if (pa_modargs_get_value(ma, "rate", NULL)) rate_set = true; if (pa_modargs_get_value(ma, "channels", NULL) || pa_modargs_get_value(ma, "channel_map", NULL)) channels_set = true; latency_msec = DEFAULT_LATENCY_MSEC; if (pa_modargs_get_value_u32(ma, "latency_msec", &latency_msec) < 0 || latency_msec < 1 || latency_msec > 2000) { pa_log("Invalid latency specification"); goto fail; } m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; u->latency = (pa_usec_t) latency_msec * PA_USEC_PER_MSEC; adjust_time_sec = DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC; if (pa_modargs_get_value_u32(ma, "adjust_time", &adjust_time_sec) < 0) { pa_log("Failed to parse adjust_time value"); goto fail; } if (adjust_time_sec != DEFAULT_ADJUST_TIME_USEC / PA_USEC_PER_SEC) u->adjust_time = adjust_time_sec * PA_USEC_PER_SEC; else u->adjust_time = DEFAULT_ADJUST_TIME_USEC; pa_sink_input_new_data_init(&sink_input_data); sink_input_data.driver = __FILE__; sink_input_data.module = m; if (sink) pa_sink_input_new_data_set_sink(&sink_input_data, sink, FALSE); if (pa_modargs_get_proplist(ma, "sink_input_properties", sink_input_data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Failed to parse the sink_input_properties value."); pa_sink_input_new_data_done(&sink_input_data); goto fail; } if (!pa_proplist_contains(sink_input_data.proplist, PA_PROP_MEDIA_ROLE)) pa_proplist_sets(sink_input_data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); pa_sink_input_new_data_set_sample_spec(&sink_input_data, &ss); pa_sink_input_new_data_set_channel_map(&sink_input_data, &map); sink_input_data.flags = PA_SINK_INPUT_VARIABLE_RATE | PA_SINK_INPUT_START_CORKED; if (!remix) sink_input_data.flags |= PA_SINK_INPUT_NO_REMIX; if (!format_set) sink_input_data.flags |= PA_SINK_INPUT_FIX_FORMAT; if (!rate_set) sink_input_data.flags |= PA_SINK_INPUT_FIX_RATE; if (!channels_set) sink_input_data.flags |= PA_SINK_INPUT_FIX_CHANNELS; sink_dont_move = FALSE; if (pa_modargs_get_value_boolean(ma, "sink_dont_move", &sink_dont_move) < 0) { pa_log("sink_dont_move= expects a boolean argument."); goto fail; } if (sink_dont_move) sink_input_data.flags |= PA_SINK_INPUT_DONT_MOVE; pa_sink_input_new(&u->sink_input, m->core, &sink_input_data); pa_sink_input_new_data_done(&sink_input_data); if (!u->sink_input) goto fail; /* If format, rate or channels were originally unset, they are set now * after the pa_sink_input_new() call. */ ss = u->sink_input->sample_spec; map = u->sink_input->channel_map; u->sink_input->parent.process_msg = sink_input_process_msg_cb; u->sink_input->pop = sink_input_pop_cb; u->sink_input->process_rewind = sink_input_process_rewind_cb; u->sink_input->kill = sink_input_kill_cb; u->sink_input->state_change = sink_input_state_change_cb; u->sink_input->attach = sink_input_attach_cb; u->sink_input->detach = sink_input_detach_cb; u->sink_input->update_max_rewind = sink_input_update_max_rewind_cb; u->sink_input->update_max_request = sink_input_update_max_request_cb; u->sink_input->may_move_to = sink_input_may_move_to_cb; u->sink_input->moving = sink_input_moving_cb; u->sink_input->suspend = sink_input_suspend_cb; u->sink_input->userdata = u; pa_sink_input_set_requested_latency(u->sink_input, u->latency/3); pa_source_output_new_data_init(&source_output_data); source_output_data.driver = __FILE__; source_output_data.module = m; if (source) pa_source_output_new_data_set_source(&source_output_data, source, FALSE); if (pa_modargs_get_proplist(ma, "source_output_properties", source_output_data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Failed to parse the source_output_properties value."); pa_source_output_new_data_done(&source_output_data); goto fail; } if (!pa_proplist_contains(source_output_data.proplist, PA_PROP_MEDIA_ROLE)) pa_proplist_sets(source_output_data.proplist, PA_PROP_MEDIA_ROLE, "abstract"); pa_source_output_new_data_set_sample_spec(&source_output_data, &ss); pa_source_output_new_data_set_channel_map(&source_output_data, &map); source_output_data.flags = PA_SOURCE_OUTPUT_START_CORKED; if (!remix) source_output_data.flags |= PA_SOURCE_OUTPUT_NO_REMIX; source_dont_move = FALSE; if (pa_modargs_get_value_boolean(ma, "source_dont_move", &source_dont_move) < 0) { pa_log("source_dont_move= expects a boolean argument."); goto fail; } if (source_dont_move) source_output_data.flags |= PA_SOURCE_OUTPUT_DONT_MOVE; pa_source_output_new(&u->source_output, m->core, &source_output_data); pa_source_output_new_data_done(&source_output_data); if (!u->source_output) goto fail; u->source_output->parent.process_msg = source_output_process_msg_cb; u->source_output->push = source_output_push_cb; u->source_output->process_rewind = source_output_process_rewind_cb; u->source_output->kill = source_output_kill_cb; u->source_output->attach = source_output_attach_cb; u->source_output->detach = source_output_detach_cb; u->source_output->state_change = source_output_state_change_cb; u->source_output->may_move_to = source_output_may_move_to_cb; u->source_output->moving = source_output_moving_cb; u->source_output->suspend = source_output_suspend_cb; u->source_output->userdata = u; pa_source_output_set_requested_latency(u->source_output, u->latency/3); pa_sink_input_get_silence(u->sink_input, &silence); u->memblockq = pa_memblockq_new( "module-loopback memblockq", 0, /* idx */ MEMBLOCKQ_MAXLENGTH, /* maxlength */ MEMBLOCKQ_MAXLENGTH, /* tlength */ &ss, /* sample_spec */ 0, /* prebuf */ 0, /* minreq */ 0, /* maxrewind */ &silence); /* silence frame */ pa_memblock_unref(silence.memblock); u->asyncmsgq = pa_asyncmsgq_new(0); if (!pa_proplist_contains(u->source_output->proplist, PA_PROP_MEDIA_NAME)) pa_proplist_setf(u->source_output->proplist, PA_PROP_MEDIA_NAME, "Loopback to %s", pa_strnull(pa_proplist_gets(u->sink_input->sink->proplist, PA_PROP_DEVICE_DESCRIPTION))); if (!pa_proplist_contains(u->source_output->proplist, PA_PROP_MEDIA_ICON_NAME) && (n = pa_proplist_gets(u->sink_input->sink->proplist, PA_PROP_DEVICE_ICON_NAME))) pa_proplist_sets(u->source_output->proplist, PA_PROP_MEDIA_ICON_NAME, n); if (!pa_proplist_contains(u->sink_input->proplist, PA_PROP_MEDIA_NAME)) pa_proplist_setf(u->sink_input->proplist, PA_PROP_MEDIA_NAME, "Loopback from %s", pa_strnull(pa_proplist_gets(u->source_output->source->proplist, PA_PROP_DEVICE_DESCRIPTION))); if (source && !pa_proplist_contains(u->sink_input->proplist, PA_PROP_MEDIA_ICON_NAME) && (n = pa_proplist_gets(u->source_output->source->proplist, PA_PROP_DEVICE_ICON_NAME))) pa_proplist_sets(u->sink_input->proplist, PA_PROP_MEDIA_ICON_NAME, n); pa_sink_input_put(u->sink_input); pa_source_output_put(u->source_output); if (pa_source_get_state(u->source_output->source) != PA_SOURCE_SUSPENDED) pa_sink_input_cork(u->sink_input, FALSE); if (pa_sink_get_state(u->sink_input->sink) != PA_SINK_SUSPENDED) pa_source_output_cork(u->source_output, FALSE); update_adjust_timer(u); pa_modargs_free(ma); return 0; fail: if (ma) pa_modargs_free(ma); pa__done(m); return -1; }
/* Called from input thread context */ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { struct userdata *u; pa_source_output_assert_ref(o); pa_source_output_assert_io_context(o); pa_assert_se(u = o->userdata); if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) { pa_log("push when no link?"); return; } /* PUT YOUR CODE HERE TO DO SOMETHING WITH THE SOURCE DATA */ /* if uplink sink exists, pull data from there; simplify by using same length as chunk provided by source */ if(u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) { pa_memchunk tchunk; size_t nbytes = chunk->length; pa_mix_info streams[2]; pa_memchunk target_chunk; void *target; int ch; /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); /* get data from the sink */ while (pa_memblockq_peek(u->sink_memblockq, &tchunk) < 0) { pa_memchunk nchunk; /* make sure we get nbytes from the sink with render_full, otherwise we cannot mix with the uplink */ pa_sink_render_full(u->sink, nbytes, &nchunk); pa_memblockq_push(u->sink_memblockq, &nchunk); pa_memblock_unref(nchunk.memblock); } pa_assert(tchunk.length == chunk->length); /* move the read pointer for sink memblockq */ pa_memblockq_drop(u->sink_memblockq, tchunk.length); /* allocate target chunk */ /* this could probably be done in-place, but having chunk as both the input and output creates issues with reference counts */ target_chunk.index = 0; target_chunk.length = chunk->length; pa_assert(target_chunk.length == chunk->length); target_chunk.memblock = pa_memblock_new(o->source->core->mempool, target_chunk.length); pa_assert( target_chunk.memblock ); /* get target pointer */ target = (void*)((uint8_t*)pa_memblock_acquire(target_chunk.memblock) + target_chunk.index); /* set-up mixing structure volume was taken care of in sink and source already */ streams[0].chunk = *chunk; for(ch=0;ch<o->sample_spec.channels;ch++) streams[0].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */ streams[0].volume.channels = o->sample_spec.channels; streams[1].chunk = tchunk; for(ch=0;ch<o->sample_spec.channels;ch++) streams[1].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */ streams[1].volume.channels = o->sample_spec.channels; /* do mixing */ pa_mix(streams, /* 2 streams to be mixed */ 2, target, /* put result in target chunk */ chunk->length, /* same length as input */ (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */ NULL, /* no volume information */ FALSE); /* no mute */ pa_memblock_release(target_chunk.memblock); pa_memblock_unref(tchunk.memblock); /* clean-up */ /* forward the data to the virtual source */ pa_source_post(u->source, &target_chunk); pa_memblock_unref(target_chunk.memblock); /* clean-up */ } else { /* forward the data to the virtual source */ pa_source_post(u->source, chunk); } }
char *pa_sink_list_to_string(pa_core *c) { pa_strbuf *s; pa_sink *sink; uint32_t idx = PA_IDXSET_INVALID; pa_assert(c); s = pa_strbuf_new(); pa_strbuf_printf(s, "%u sink(s) available.\n", pa_idxset_size(c->sinks)); for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) { char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cvdb[PA_SW_CVOLUME_SNPRINT_DB_MAX], v[PA_VOLUME_SNPRINT_MAX], vdb[PA_SW_VOLUME_SNPRINT_DB_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], *t; const char *cmn; cmn = pa_channel_map_to_pretty_name(&sink->channel_map); pa_strbuf_printf( s, " %c index: %u\n" "\tname: <%s>\n" "\tdriver: <%s>\n" "\tflags: %s%s%s%s%s%s%s%s\n" "\tstate: %s\n" "\tsuspend cause: %s%s%s%s\n" "\tpriority: %u\n" "\tvolume: %s%s%s\n" "\t balance %0.2f\n" "\tbase volume: %s%s%s\n" "\tvolume steps: %u\n" "\tmuted: %s\n" "\tcurrent latency: %0.2f ms\n" "\tmax request: %lu KiB\n" "\tmax rewind: %lu KiB\n" "\tmonitor source: %u\n" "\tsample spec: %s\n" "\tchannel map: %s%s%s\n" "\tused by: %u\n" "\tlinked by: %u\n", sink == c->default_sink ? '*' : ' ', sink->index, sink->name, sink->driver, sink->flags & PA_SINK_HARDWARE ? "HARDWARE " : "", sink->flags & PA_SINK_NETWORK ? "NETWORK " : "", sink->flags & PA_SINK_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "", sink->flags & PA_SINK_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", sink->flags & PA_SINK_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", sink->flags & PA_SINK_LATENCY ? "LATENCY " : "", sink->flags & PA_SINK_FLAT_VOLUME ? "FLAT_VOLUME " : "", sink->flags & PA_SINK_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "", sink_state_to_string(pa_sink_get_state(sink)), sink->suspend_cause & PA_SUSPEND_USER ? "USER " : "", sink->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "", sink->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "", sink->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "", sink->priority, pa_cvolume_snprint(cv, sizeof(cv), pa_sink_get_volume(sink, FALSE)), sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "", sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_sink_get_volume(sink, FALSE)) : "", pa_cvolume_get_balance(pa_sink_get_volume(sink, FALSE), &sink->channel_map), pa_volume_snprint(v, sizeof(v), sink->base_volume), sink->flags & PA_SINK_DECIBEL_VOLUME ? "\n\t " : "", sink->flags & PA_SINK_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), sink->base_volume) : "", sink->n_volume_steps, pa_yes_no(pa_sink_get_mute(sink, FALSE)), (double) pa_sink_get_latency(sink) / (double) PA_USEC_PER_MSEC, (unsigned long) pa_sink_get_max_request(sink) / 1024, (unsigned long) pa_sink_get_max_rewind(sink) / 1024, sink->monitor_source ? sink->monitor_source->index : PA_INVALID_INDEX, pa_sample_spec_snprint(ss, sizeof(ss), &sink->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &sink->channel_map), cmn ? "\n\t " : "", cmn ? cmn : "", pa_sink_used_by(sink), pa_sink_linked_by(sink)); if (sink->flags & PA_SINK_DYNAMIC_LATENCY) { pa_usec_t min_latency, max_latency; pa_sink_get_latency_range(sink, &min_latency, &max_latency); pa_strbuf_printf( s, "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n", (double) pa_sink_get_requested_latency(sink) / (double) PA_USEC_PER_MSEC, (double) min_latency / PA_USEC_PER_MSEC, (double) max_latency / PA_USEC_PER_MSEC); } else pa_strbuf_printf( s, "\tfixed latency: %0.2f ms\n", (double) pa_sink_get_fixed_latency(sink) / PA_USEC_PER_MSEC); if (sink->card) pa_strbuf_printf(s, "\tcard: %u <%s>\n", sink->card->index, sink->card->name); if (sink->module) pa_strbuf_printf(s, "\tmodule: %u\n", sink->module->index); t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t"); pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); append_port_list(s, sink->ports); if (sink->active_port) pa_strbuf_printf( s, "\tactive port: <%s>\n", sink->active_port->name); } return pa_strbuf_tostring_free(s); }
/* Generic sink state change logic. Used by raw_sink and voip_sink */ int voice_sink_set_state(pa_sink *s, pa_sink *other, pa_sink_state_t state) { struct userdata *u; pa_sink *om_sink; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); if (!other) { pa_log_debug("other sink not initialized or freed"); return 0; } pa_sink_assert_ref(other); om_sink = u->master_sink; if (u->hw_sink_input && PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(u->hw_sink_input))) { if (pa_sink_input_get_state(u->hw_sink_input) == PA_SINK_INPUT_CORKED) { if (PA_SINK_IS_OPENED(state) || PA_SINK_IS_OPENED(pa_sink_get_state(other)) || pa_atomic_load(&u->cmt_connection.dl_state) == CMT_DL_ACTIVE) { pa_sink_input_cork(u->hw_sink_input, FALSE); pa_log_debug("hw_sink_input uncorked"); } } else { if (state == PA_SINK_SUSPENDED && pa_sink_get_state(other) == PA_SINK_SUSPENDED && pa_atomic_load(&u->cmt_connection.dl_state) != CMT_DL_ACTIVE) { pa_sink_input_cork(u->hw_sink_input, TRUE); pa_log_debug("hw_sink_input corked"); } } } if (om_sink == NULL) { pa_log_info("No master sink, assuming primary mixer tuning.\n"); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI); } else if (pa_atomic_load(&u->cmt_connection.dl_state) == CMT_DL_ACTIVE || (pa_sink_get_state(u->voip_sink) <= PA_SINK_SUSPENDED && voice_voip_sink_used_by(u))) { if (pa_atomic_load(&u->mixer_state) == PROP_MIXER_TUNING_PRI) { pa_proplist *p = pa_proplist_new(); pa_assert(p); pa_proplist_sets(p, PROP_MIXER_TUNING_MODE, PROP_MIXER_TUNING_ALT_S); pa_sink_update_proplist(om_sink, PA_UPDATE_REPLACE, p); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_ALT); pa_proplist_free(p); if (u->sidetone_enable) voice_enable_sidetone(u,1); } } else { if (pa_atomic_load(&u->mixer_state) == PROP_MIXER_TUNING_ALT) { pa_proplist *p = pa_proplist_new(); pa_assert(p); pa_proplist_sets(p, PROP_MIXER_TUNING_MODE, PROP_MIXER_TUNING_PRI_S); pa_sink_update_proplist(om_sink, PA_UPDATE_REPLACE, p); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI); pa_proplist_free(p); voice_enable_sidetone(u,0); } } return 0; }