static void master_source_state_subscribe_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { struct userdata *u = userdata; pa_assert(c); pa_assert(u); if ((t & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) return; if (!u->master_source) return; if (u->master_source != pa_idxset_get_by_index(c->sources, idx)) return; if (pa_source_get_state(u->master_source) == u->previous_master_source_state) return; u->previous_master_source_state = pa_source_get_state(u->master_source); if (u->previous_master_source_state == PA_SOURCE_SUSPENDED) { meego_algorithm_hook_fire(u->hooks[HOOK_SOURCE_RESET], NULL); pa_log_debug("VOICE_HOOK_SOURCE_RESET fired"); voice_aep_ear_ref_loop_reset(u); } }
/* Called from main thread */ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) { struct userdata *u; char *input_description; const char *n; if (!dest) return; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert_se(u = o->userdata); input_description = pa_sprintf_malloc("Loopback of %s", pa_strnull(pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_DESCRIPTION))); pa_sink_input_set_property(u->sink_input, PA_PROP_MEDIA_NAME, input_description); pa_xfree(input_description); if ((n = pa_proplist_gets(dest->proplist, PA_PROP_DEVICE_ICON_NAME))) pa_sink_input_set_property(u->sink_input, PA_PROP_DEVICE_ICON_NAME, n); if (pa_source_get_state(dest) == PA_SOURCE_SUSPENDED) pa_sink_input_cork(u->sink_input, true); else pa_sink_input_cork(u->sink_input, 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; }
/* Called from main thread */ static void source_output_moving_cb(pa_source_output *o, pa_source *dest) { pa_proplist *p; const char *n; struct userdata *u; if (!dest) return; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert_se(u = o->userdata); p = pa_proplist_new(); pa_proplist_setf(p, PA_PROP_MEDIA_NAME, "Loopback of %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_sink_input_update_proplist(u->sink_input, PA_UPDATE_REPLACE, p); pa_proplist_free(p); if (pa_source_get_state(dest) == PA_SOURCE_SUSPENDED) pa_sink_input_cork(u->sink_input, true); else pa_sink_input_cork(u->sink_input, 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 source_get_mute_cb(pa_source *s) { struct userdata *u; pa_source_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) || !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) return; /* FIXME, no volume control in source_output, get the info from the master */ pa_source_get_mute(u->source_output->source, TRUE); }
/* Generic source state change logic. Used by raw_source and voice_source */ int voice_source_set_state(pa_source *s, pa_source *other, pa_source_state_t state) { struct userdata *u; pa_source_assert_ref(s); pa_assert_se(u = s->userdata); if (!other) { pa_log_debug("other source not initialized or freed"); return 0; } pa_source_assert_ref(other); if (u->hw_source_output) { if (pa_source_output_get_state(u->hw_source_output) == PA_SOURCE_OUTPUT_RUNNING) { if (state == PA_SOURCE_SUSPENDED && pa_source_get_state(other) == PA_SOURCE_SUSPENDED && pa_atomic_load(&u->cmt_connection.ul_state) != CMT_UL_ACTIVE) { pa_source_output_cork(u->hw_source_output, TRUE); pa_log_debug("hw_source_output corked"); } } else if (pa_source_output_get_state(u->hw_source_output) == PA_SOURCE_OUTPUT_CORKED) { if (PA_SOURCE_IS_OPENED(state) || PA_SOURCE_IS_OPENED(pa_source_get_state(other)) || pa_atomic_load(&u->cmt_connection.ul_state) == CMT_UL_ACTIVE) { pa_source_output_cork(u->hw_source_output, FALSE); pa_log_debug("hw_source_output uncorked"); } } } if (pa_atomic_load(&u->cmt_connection.ul_state) != CMT_UL_ACTIVE && !PA_SOURCE_IS_OPENED(pa_source_get_state(u->voip_source))) { voice_aep_ear_ref_loop_reset(u); } return 0; }
static void source_get_volume_cb(pa_source *s) { struct userdata *u; pa_source_assert_ref(s); pa_assert_se(u = s->userdata); if (!PA_SOURCE_IS_LINKED(pa_source_get_state(s)) || !PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) return; /* FIXME, no volume control in source_output, get the info from the master */ pa_source_get_volume(u->source_output->source, TRUE); if (pa_cvolume_equal(&s->volume,&u->source_output->source->volume)) /* no change */ return; s->volume = u->source_output->source->volume; pa_source_set_soft_volume(s, NULL); }
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; }
char *pa_source_list_to_string(pa_core *c) { pa_strbuf *s; pa_source *source; uint32_t idx = PA_IDXSET_INVALID; static const char* const state_table[] = { [PA_SOURCE_RUNNING] = "RUNNING", [PA_SOURCE_SUSPENDED] = "SUSPENDED", [PA_SOURCE_IDLE] = "IDLE", [PA_SOURCE_UNLINKED] = "UNLINKED" }; pa_assert(c); s = pa_strbuf_new(); pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources)); for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) { char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX], cv[PA_CVOLUME_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: <%u>\n" "\tlatency: <%0.0f usec>\n" "\tsample spec: <%s>\n" "\tchannel map: <%s>\n" "\tused by: <%u>\n" "\tlinked by: <%u>\n", c->default_source_name && !strcmp(source->name, c->default_source_name) ? '*' : ' ', source->index, source->name, source->driver, source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "", source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "", source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "", state_table[pa_source_get_state(source)], pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source)), !!pa_source_get_mute(source), (double) pa_source_get_latency(source), pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map), pa_source_used_by(source), pa_source_linked_by(source)); if (source->monitor_of) pa_strbuf_printf(s, "\tmonitor_of: <%u>\n", source->monitor_of->index); if (source->module) pa_strbuf_printf(s, "\tmodule: <%u>\n", source->module->index); if (source->description) pa_strbuf_printf(s, "\tdescription: <%s>\n", source->description); } return pa_strbuf_tostring_free(s); }
int pa__init(pa_module*m) { pa_modargs *ma = NULL; struct userdata *u; const char *master_sink_name; const char *master_source_name; const char *raw_sink_name; const char *raw_source_name; const char *voice_sink_name; const char *voice_source_name; const char *max_hw_frag_size_str; int max_hw_frag_size = 3840; pa_sink *master_sink; pa_source *master_source; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments"); goto fail; } master_sink_name = pa_modargs_get_value(ma, "master_sink", NULL); master_source_name = pa_modargs_get_value(ma, "master_source", NULL); raw_sink_name = pa_modargs_get_value(ma, "raw_sink_name", "sink.voice.raw"); raw_source_name = pa_modargs_get_value(ma, "raw_source_name", "source.voice.raw"); voice_sink_name = pa_modargs_get_value(ma, "voice_sink_name", "sink.voice"); voice_source_name = pa_modargs_get_value(ma, "voice_source_name", "source.voice"); max_hw_frag_size_str = pa_modargs_get_value(ma, "max_hw_frag_size", "3840"); pa_log_debug("Got arguments: master_sink=\"%s\" master_source=\"%s\" " "raw_sink_name=\"%s\" raw_source_name=\"%s\" max_hw_frag_size=\"%s\".", master_sink_name, master_source_name, raw_sink_name, raw_source_name, max_hw_frag_size_str); if (!(master_sink = pa_namereg_get(m->core, master_sink_name, PA_NAMEREG_SINK))) { pa_log("Master sink \"%s\" not found", master_sink_name); goto fail; } if (!(master_source = pa_namereg_get(m->core, master_source_name, PA_NAMEREG_SOURCE))) { pa_log("Master source \"%s\" not found", master_source_name); goto fail; } if (master_sink->sample_spec.format != master_source->sample_spec.format && master_sink->sample_spec.rate != master_source->sample_spec.rate && master_sink->sample_spec.channels != master_source->sample_spec.channels) { pa_log("Master source and sink must have same sample spec"); goto fail; } if (pa_atoi(max_hw_frag_size_str, &max_hw_frag_size) < 0 || max_hw_frag_size < 960 || max_hw_frag_size > 128*960) { pa_log("Bad value for max_hw_frag_size: %s", max_hw_frag_size_str); goto fail; } m->userdata = u = pa_xnew0(struct userdata, 1); u->modargs = ma; u->core = m->core; u->module = m; u->master_sink = master_sink; u->master_source = master_source; set_hooks(u); u->mainloop_handler = voice_mainloop_handler_new(u); u->ul_timing_advance = 500; // = 500 micro seconds, seems to be a good default value pa_channel_map_init_mono(&u->mono_map); pa_channel_map_init_stereo(&u->stereo_map); u->hw_sample_spec.format = PA_SAMPLE_S16NE; u->hw_sample_spec.rate = VOICE_SAMPLE_RATE_HW_HZ; u->hw_sample_spec.channels = 2; u->hw_mono_sample_spec.format = PA_SAMPLE_S16NE; u->hw_mono_sample_spec.rate = VOICE_SAMPLE_RATE_HW_HZ; u->hw_mono_sample_spec.channels = 1; u->aep_sample_spec.format = PA_SAMPLE_S16NE; u->aep_sample_spec.rate = VOICE_SAMPLE_RATE_AEP_HZ; u->aep_sample_spec.channels = 1; pa_channel_map_init_mono(&u->aep_channel_map); // The result is rounded down incorrectly thus +1 u->aep_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->aep_sample_spec); u->aep_hw_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->hw_sample_spec); u->hw_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_MASTER_USECS+1, &u->hw_sample_spec); u->hw_fragment_size_max = max_hw_frag_size; if (0 != (u->hw_fragment_size_max % u->hw_fragment_size)) u->hw_fragment_size_max += u->hw_fragment_size - (u->hw_fragment_size_max % u->hw_fragment_size); u->aep_hw_mono_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_AEP_USECS+1, &u->hw_mono_sample_spec); u->hw_mono_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_MASTER_USECS+1, &u->hw_mono_sample_spec); u->voice_ul_fragment_size = pa_usec_to_bytes(VOICE_PERIOD_CMT_USECS+1, &u->aep_sample_spec); pa_silence_memchunk_get(&u->core->silence_cache, u->core->mempool, &u->aep_silence_memchunk, & u->aep_sample_spec, u->aep_fragment_size); voice_memchunk_pool_load(u); if (voice_init_raw_sink(u, raw_sink_name)) goto fail; u->call_state_tracker = pa_call_state_tracker_get(m->core); pa_atomic_store(&u->mixer_state, PROP_MIXER_TUNING_PRI); pa_call_state_tracker_set_active(u->call_state_tracker, FALSE); u->alt_mixer_compensation = PA_VOLUME_NORM; if (voice_init_hw_sink_input(u)) goto fail; /* This must be set before calling pa_sink_put(), because pa_sink_put() has * assertion * "!(s->flags & PA_SINK_SHARE_VOLUME_WITH_MASTER) || s->flat_sink_input". */ u->raw_sink->flat_sink_input = u->hw_sink_input; /* This must be called before calling voice_init_voip_sink(), because * pa_sink_input_new() has assertion * "PA_SINK_IS_LINKED(pa_sink_get_state(data->sink))". */ pa_sink_put(u->raw_sink); /* This must be called before calling voice_init_aep_sink_input(), because * the flat volume logic will otherwise mess up the aep sink input's volume * when pa_sink_input_put(u->hw_sink_input) is called. */ pa_sink_input_put(u->hw_sink_input); if (voice_init_voip_sink(u, voice_sink_name)) goto fail; if (voice_init_aep_sink_input(u)) goto fail; u->sink_temp_buff = pa_xmalloc(2*u->hw_fragment_size_max); u->sink_temp_buff_len = 2*u->hw_fragment_size_max; if (voice_init_raw_source(u, raw_source_name)) goto fail; pa_source_put(u->raw_source); if (voice_init_voip_source(u, voice_source_name)) goto fail; pa_source_put(u->voip_source); if (voice_init_hw_source_output(u)) goto fail; /* TODO: Guess we should use max_hw_frag_size here */ u->hw_source_memblockq = // 8 * 5ms = 40ms pa_memblockq_new(0, 2*u->hw_fragment_size_max, 0, pa_frame_size(&u->hw_sample_spec), 0, 0, 0, NULL); u->ul_memblockq = pa_memblockq_new(0, 2*u->voice_ul_fragment_size, 0, pa_frame_size(&u->aep_sample_spec), 0, 0, 0, NULL); u->dl_sideinfo_queue = pa_queue_new(); u->ul_deadline = 0; u->linear_q15_master_volume_L = INT16_MAX; u->linear_q15_master_volume_R = INT16_MAX; voice_aep_ear_ref_init(u); if (voice_convert_init(u)) goto fail; /* IHF mode is the default and this initialization is consistent with it. */ u->active_mic_channel = MIC_CH0; meego_parameter_request_updates("voice", (pa_hook_cb_t)voice_parameter_cb, PA_HOOK_NORMAL, FALSE, u); meego_parameter_request_updates("alsa", (pa_hook_cb_t)alsa_parameter_cb, PA_HOOK_NORMAL, FALSE, u); meego_parameter_request_updates("aep", (pa_hook_cb_t)aep_parameter_cb, PA_HOOK_LATE, FALSE, u); /* aep-s-i */ /* voip-sink ---\ hw-sink-input */ /* > optimized mix -------------> master-sink */ /* | */ /* raw-sink */ /* */ /* voip-src <--- hw-source-output */ /* < mux <------------- master-src */ /* raw-src <--- */ u->voip_sink->flat_sink_input = u->aep_sink_input; pa_sink_put(u->voip_sink); pa_source_output_put(u->hw_source_output); pa_sink_input_put(u->aep_sink_input); u->sink_subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SINK_INPUT, master_sink_volume_subscribe_cb, u); u->previous_master_source_state = pa_source_get_state(u->master_source); u->source_change_subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SOURCE, master_source_state_subscribe_cb, u); return 0; fail: pa__done(m); return -1; }
/* Called from main context */ int pa_source_output_new( pa_source_output**_o, pa_core *core, pa_source_output_new_data *data) { pa_source_output *o; pa_resampler *resampler = NULL; char st[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; int r; char *pt; pa_assert(_o); pa_assert(core); pa_assert(data); pa_assert_ctl_context(); if (data->client) pa_proplist_update(data->proplist, PA_UPDATE_MERGE, data->client->proplist); if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], data)) < 0) return r; pa_return_val_if_fail(!data->driver || pa_utf8_valid(data->driver), -PA_ERR_INVALID); if (!data->source) { data->source = pa_namereg_get(core, NULL, PA_NAMEREG_SOURCE); data->save_source = FALSE; } pa_return_val_if_fail(data->source, -PA_ERR_NOENTITY); pa_return_val_if_fail(PA_SOURCE_IS_LINKED(pa_source_get_state(data->source)), -PA_ERR_BADSTATE); pa_return_val_if_fail(!data->direct_on_input || data->direct_on_input->sink == data->source->monitor_of, -PA_ERR_INVALID); if (!data->sample_spec_is_set) data->sample_spec = data->source->sample_spec; pa_return_val_if_fail(pa_sample_spec_valid(&data->sample_spec), -PA_ERR_INVALID); if (!data->channel_map_is_set) { if (pa_channel_map_compatible(&data->source->channel_map, &data->sample_spec)) data->channel_map = data->source->channel_map; else pa_channel_map_init_extend(&data->channel_map, data->sample_spec.channels, PA_CHANNEL_MAP_DEFAULT); } pa_return_val_if_fail(pa_channel_map_valid(&data->channel_map), -PA_ERR_INVALID); pa_return_val_if_fail(pa_channel_map_compatible(&data->channel_map, &data->sample_spec), -PA_ERR_INVALID); if (data->flags & PA_SOURCE_OUTPUT_FIX_FORMAT) data->sample_spec.format = data->source->sample_spec.format; if (data->flags & PA_SOURCE_OUTPUT_FIX_RATE) data->sample_spec.rate = data->source->sample_spec.rate; if (data->flags & PA_SOURCE_OUTPUT_FIX_CHANNELS) { data->sample_spec.channels = data->source->sample_spec.channels; data->channel_map = data->source->channel_map; } pa_assert(pa_sample_spec_valid(&data->sample_spec)); pa_assert(pa_channel_map_valid(&data->channel_map)); if (data->resample_method == PA_RESAMPLER_INVALID) data->resample_method = core->resample_method; pa_return_val_if_fail(data->resample_method < PA_RESAMPLER_MAX, -PA_ERR_INVALID); if ((r = pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_FIXATE], data)) < 0) return r; if ((data->flags & PA_SOURCE_OUTPUT_NO_CREATE_ON_SUSPEND) && pa_source_get_state(data->source) == PA_SOURCE_SUSPENDED) { pa_log("Failed to create source output: source is suspended."); return -PA_ERR_BADSTATE; } if (pa_idxset_size(data->source->outputs) >= PA_MAX_OUTPUTS_PER_SOURCE) { pa_log("Failed to create source output: too many outputs per source."); return -PA_ERR_TOOLARGE; } if ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) || !pa_sample_spec_equal(&data->sample_spec, &data->source->sample_spec) || !pa_channel_map_equal(&data->channel_map, &data->source->channel_map)) { if (!(resampler = pa_resampler_new( core->mempool, &data->source->sample_spec, &data->source->channel_map, &data->sample_spec, &data->channel_map, data->resample_method, ((data->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((data->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (core->disable_remixing || (data->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0) | (core->disable_lfe_remixing ? PA_RESAMPLER_NO_LFE : 0)))) { pa_log_warn("Unsupported resampling operation."); return -PA_ERR_NOTSUPPORTED; } } o = pa_msgobject_new(pa_source_output); o->parent.parent.free = source_output_free; o->parent.process_msg = pa_source_output_process_msg; o->core = core; o->state = PA_SOURCE_OUTPUT_INIT; o->flags = data->flags; o->proplist = pa_proplist_copy(data->proplist); o->driver = pa_xstrdup(pa_path_get_filename(data->driver)); o->module = data->module; o->source = data->source; o->destination_source = data->destination_source; o->client = data->client; o->actual_resample_method = resampler ? pa_resampler_get_method(resampler) : PA_RESAMPLER_INVALID; o->requested_resample_method = data->resample_method; o->sample_spec = data->sample_spec; o->channel_map = data->channel_map; o->direct_on_input = data->direct_on_input; o->save_source = data->save_source; reset_callbacks(o); o->userdata = NULL; o->thread_info.state = o->state; o->thread_info.attached = FALSE; o->thread_info.sample_spec = o->sample_spec; o->thread_info.resampler = resampler; o->thread_info.requested_source_latency = (pa_usec_t) -1; o->thread_info.direct_on_input = o->direct_on_input; o->thread_info.delay_memblockq = pa_memblockq_new( 0, MEMBLOCKQ_MAXLENGTH, 0, pa_frame_size(&o->source->sample_spec), 0, 1, 0, &o->source->silence); pa_assert_se(pa_idxset_put(core->source_outputs, o, &o->index) == 0); pa_assert_se(pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL) == 0); if (o->client) pa_assert_se(pa_idxset_put(o->client->source_outputs, o, NULL) >= 0); if (o->direct_on_input) pa_assert_se(pa_idxset_put(o->direct_on_input->direct_outputs, o, NULL) == 0); pt = pa_proplist_to_string_sep(o->proplist, "\n "); pa_log_info("Created output %u \"%s\" on %s with sample spec %s and channel map %s\n %s", o->index, pa_strnull(pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME)), o->source->name, pa_sample_spec_snprint(st, sizeof(st), &o->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &o->channel_map), pt); pa_xfree(pt); /* Don't forget to call pa_source_output_put! */ *_o = o; return 0; }
char *pa_source_list_to_string(pa_core *c) { pa_strbuf *s; pa_source *source; uint32_t idx = PA_IDXSET_INVALID; pa_assert(c); s = pa_strbuf_new(); pa_strbuf_printf(s, "%u source(s) available.\n", pa_idxset_size(c->sources)); for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &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(&source->channel_map); pa_strbuf_printf( s, " %c index: %u\n" "\tname: <%s>\n" "\tdriver: <%s>\n" "\tflags: %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 rewind: %lu KiB\n" "\tsample spec: %s\n" "\tchannel map: %s%s%s\n" "\tused by: %u\n" "\tlinked by: %u\n", c->default_source == source ? '*' : ' ', source->index, source->name, source->driver, source->flags & PA_SOURCE_HARDWARE ? "HARDWARE " : "", source->flags & PA_SOURCE_NETWORK ? "NETWORK " : "", source->flags & PA_SOURCE_HW_MUTE_CTRL ? "HW_MUTE_CTRL " : "", source->flags & PA_SOURCE_HW_VOLUME_CTRL ? "HW_VOLUME_CTRL " : "", source->flags & PA_SOURCE_DECIBEL_VOLUME ? "DECIBEL_VOLUME " : "", source->flags & PA_SOURCE_LATENCY ? "LATENCY " : "", source->flags & PA_SOURCE_DYNAMIC_LATENCY ? "DYNAMIC_LATENCY" : "", source_state_to_string(pa_source_get_state(source)), source->suspend_cause & PA_SUSPEND_USER ? "USER " : "", source->suspend_cause & PA_SUSPEND_APPLICATION ? "APPLICATION " : "", source->suspend_cause & PA_SUSPEND_IDLE ? "IDLE " : "", source->suspend_cause & PA_SUSPEND_SESSION ? "SESSION" : "", source->priority, pa_cvolume_snprint(cv, sizeof(cv), pa_source_get_volume(source, FALSE)), source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "", source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_cvolume_snprint_dB(cvdb, sizeof(cvdb), pa_source_get_volume(source, FALSE)) : "", pa_cvolume_get_balance(pa_source_get_volume(source, FALSE), &source->channel_map), pa_volume_snprint(v, sizeof(v), source->base_volume), source->flags & PA_SOURCE_DECIBEL_VOLUME ? "\n\t " : "", source->flags & PA_SOURCE_DECIBEL_VOLUME ? pa_sw_volume_snprint_dB(vdb, sizeof(vdb), source->base_volume) : "", source->n_volume_steps, pa_yes_no(pa_source_get_mute(source, FALSE)), (double) pa_source_get_latency(source) / PA_USEC_PER_MSEC, (unsigned long) pa_source_get_max_rewind(source) / 1024, pa_sample_spec_snprint(ss, sizeof(ss), &source->sample_spec), pa_channel_map_snprint(cm, sizeof(cm), &source->channel_map), cmn ? "\n\t " : "", cmn ? cmn : "", pa_source_used_by(source), pa_source_linked_by(source)); if (source->flags & PA_SOURCE_DYNAMIC_LATENCY) { pa_usec_t min_latency, max_latency; pa_source_get_latency_range(source, &min_latency, &max_latency); pa_strbuf_printf( s, "\tconfigured latency: %0.2f ms; range is %0.2f .. %0.2f ms\n", (double) pa_source_get_requested_latency(source) / 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_source_get_fixed_latency(source) / PA_USEC_PER_MSEC); if (source->monitor_of) pa_strbuf_printf(s, "\tmonitor_of: %u\n", source->monitor_of->index); if (source->card) pa_strbuf_printf(s, "\tcard: %u <%s>\n", source->card->index, source->card->name); if (source->module) pa_strbuf_printf(s, "\tmodule: %u\n", source->module->index); t = pa_proplist_to_string_sep(source->proplist, "\n\t\t"); pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t); pa_xfree(t); append_port_list(s, source->ports); if (source->active_port) pa_strbuf_printf( s, "\tactive port: <%s>\n", source->active_port->name); } return pa_strbuf_tostring_free(s); }