static pa_scache_entry* scache_add_item(pa_core *c, const char *name) { pa_scache_entry *e; pa_assert(c); pa_assert(name); if ((e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) { if (e->memchunk.memblock) pa_memblock_unref(e->memchunk.memblock); pa_xfree(e->filename); pa_proplist_clear(e->proplist); pa_assert(e->core == c); pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); } else { e = pa_xnew(pa_scache_entry, 1); if (!pa_namereg_register(c, name, PA_NAMEREG_SAMPLE, e, TRUE)) { pa_xfree(e); return NULL; } e->name = pa_xstrdup(name); e->core = c; e->proplist = pa_proplist_new(); pa_idxset_put(c->scache, e, &e->index); pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_NEW, e->index); } e->last_used_time = 0; pa_memchunk_reset(&e->memchunk); e->filename = NULL; e->lazy = FALSE; e->last_used_time = 0; pa_sample_spec_init(&e->sample_spec); pa_channel_map_init(&e->channel_map); pa_cvolume_init(&e->volume); e->volume_is_set = FALSE; pa_proplist_sets(e->proplist, PA_PROP_MEDIA_ROLE, "event"); return e; }
static pa_autoload_entry* entry_new(pa_core *c, const char *name) { pa_autoload_entry *e = NULL; pa_core_assert_ref(c); pa_assert(name); if (c->autoload_hashmap && (e = pa_hashmap_get(c->autoload_hashmap, name))) return NULL; e = pa_xnew(pa_autoload_entry, 1); e->core = c; e->name = pa_xstrdup(name); e->module = e->argument = NULL; e->in_action = 0; if (!c->autoload_hashmap) c->autoload_hashmap = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func); pa_assert(c->autoload_hashmap); pa_hashmap_put(c->autoload_hashmap, e->name, e); if (!c->autoload_idxset) c->autoload_idxset = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func); pa_idxset_put(c->autoload_idxset, e, &e->index); pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_NEW, e->index); return e; }
void pa_client_free(pa_client *c) { pa_core *core; pa_assert(c); pa_assert(c->core); core = c->core; pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_UNLINK], c); pa_idxset_remove_by_data(c->core->clients, c, NULL); pa_log_info("Freed %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME))); pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_REMOVE, c->index); pa_assert(pa_idxset_isempty(c->sink_inputs)); pa_idxset_free(c->sink_inputs, NULL); pa_assert(pa_idxset_isempty(c->source_outputs)); pa_idxset_free(c->source_outputs, NULL); pa_proplist_free(c->proplist); pa_xfree(c->driver); pa_xfree(c); pa_core_check_idle(core); }
pa_client *pa_client_new(pa_core *core, pa_client_new_data *data) { pa_client *c; pa_core_assert_ref(core); pa_assert(data); if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_NEW], data) < 0) return NULL; c = pa_xnew(pa_client, 1); c->core = core; c->proplist = pa_proplist_copy(data->proplist); c->driver = pa_xstrdup(pa_path_get_filename(data->driver)); c->module = data->module; c->sink_inputs = pa_idxset_new(NULL, NULL); c->source_outputs = pa_idxset_new(NULL, NULL); c->userdata = NULL; c->kill = NULL; c->send_event = NULL; pa_assert_se(pa_idxset_put(core->clients, c, &c->index) >= 0); pa_log_info("Created %u \"%s\"", c->index, pa_strnull(pa_proplist_gets(c->proplist, PA_PROP_APPLICATION_NAME))); pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_NEW, c->index); pa_hook_fire(&core->hooks[PA_CORE_HOOK_CLIENT_PUT], c); pa_core_check_idle(core); return c; }
void pa_scache_unload_unused(pa_core *c) { pa_scache_entry *e; time_t now; uint32_t idx; pa_assert(c); if (!c->scache || !pa_idxset_size(c->scache)) return; time(&now); for (e = pa_idxset_first(c->scache, &idx); e; e = pa_idxset_next(c->scache, &idx)) { if (!e->lazy || !e->memchunk.memblock) continue; if (e->last_used_time + c->scache_idle_time > now) continue; pa_memblock_unref(e->memchunk.memblock); pa_memchunk_reset(&e->memchunk); pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); } }
void pa_module_update_proplist(pa_module *m, pa_update_mode_t mode, pa_proplist *p) { pa_assert(m); if (p) pa_proplist_update(m->proplist, mode, p); pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_CHANGE, m->index); }
static void entry_free(pa_autoload_entry *e) { pa_assert(e); pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_AUTOLOAD|PA_SUBSCRIPTION_EVENT_REMOVE, PA_INVALID_INDEX); pa_xfree(e->name); pa_xfree(e->module); pa_xfree(e->argument); pa_xfree(e); }
void pa_client_update_proplist(pa_client *c, pa_update_mode_t mode, pa_proplist *p) { pa_assert(c); if (p) pa_proplist_update(c->proplist, mode, p); pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CLIENT_PROPLIST_CHANGED], c); pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CLIENT|PA_SUBSCRIPTION_EVENT_CHANGE, c->index); }
/* Called from main thread */ void pa_source_output_update_proplist(pa_source_output *o, pa_update_mode_t mode, pa_proplist *p) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); if (p) pa_proplist_update(o->proplist, mode, p); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) { pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } }
static void free_entry(pa_scache_entry *e) { pa_assert(e); pa_namereg_unregister(e->core, e->name); pa_subscription_post(e->core, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_REMOVE, e->index); pa_xfree(e->name); pa_xfree(e->filename); if (e->memchunk.memblock) pa_memblock_unref(e->memchunk.memblock); if (e->proplist) pa_proplist_free(e->proplist); pa_xfree(e); }
void pa_device_port_set_available(pa_device_port *p, pa_port_available_t status) { uint32_t state; pa_card *card; /* pa_source *source; pa_sink *sink; */ pa_core *core; pa_assert(p); if (p->available == status) return; /* pa_assert(status != PA_PORT_AVAILABLE_UNKNOWN); */ p->available = status; pa_log_debug("Setting port %s to status %s", p->name, status == PA_PORT_AVAILABLE_YES ? "yes" : status == PA_PORT_AVAILABLE_NO ? "no" : "unknown"); /* Post subscriptions to the card which owns us */ pa_assert_se(core = p->core); PA_IDXSET_FOREACH(card, core->cards, state) if (p == pa_hashmap_get(card->ports, p->name)) pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, card->index); #if 0 /* This stuff is temporarily commented out while figuring out whether to actually do this */ if (p->is_output) PA_IDXSET_FOREACH(sink, core->sinks, state) if (p == pa_hashmap_get(sink->ports, p->name)) pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, sink->index); if (p->is_input) PA_IDXSET_FOREACH(source, core->sources, state) if (p == pa_hashmap_get(source->ports, p->name)) pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, source->index); #endif pa_hook_fire(&core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p); }
/* Called from main context */ void pa_source_output_unlink(pa_source_output*o) { pa_bool_t linked; pa_assert(o); pa_assert_ctl_context(); /* See pa_sink_unlink() for a couple of comments how this function * works */ pa_source_output_ref(o); linked = PA_SOURCE_OUTPUT_IS_LINKED(o->state); if (linked) pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK], o); if (o->direct_on_input) pa_idxset_remove_by_data(o->direct_on_input->direct_outputs, o, NULL); pa_idxset_remove_by_data(o->core->source_outputs, o, NULL); if (o->source) if (pa_idxset_remove_by_data(o->source->outputs, o, NULL)) pa_source_output_unref(o); if (o->client) pa_idxset_remove_by_data(o->client->source_outputs, o, NULL); update_n_corked(o, PA_SOURCE_OUTPUT_UNLINKED); o->state = PA_SOURCE_OUTPUT_UNLINKED; if (linked && o->source) if (o->source->asyncmsgq) pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0); reset_callbacks(o); if (linked) { pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_REMOVE, o->index); pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_UNLINK_POST], o); } if (o->source) { pa_source_update_status(o->source); o->source = NULL; } pa_core_maybe_vacuum(o->core); pa_source_output_unref(o); }
/* Called from main context */ int pa_source_output_set_rate(pa_source_output *o, uint32_t rate) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_return_val_if_fail(o->thread_info.resampler, -PA_ERR_BADSTATE); if (o->sample_spec.rate == rate) return 0; o->sample_spec.rate = rate; pa_asyncmsgq_post(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_RATE, PA_UINT_TO_PTR(rate), 0, NULL, NULL); pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); return 0; }
/* Called from main context */ static void source_output_set_state(pa_source_output *o, pa_source_output_state_t state) { pa_assert(o); pa_assert_ctl_context(); if (o->state == state) return; pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_STATE, PA_UINT_TO_PTR(state), 0, NULL) == 0); update_n_corked(o, state); o->state = state; if (state != PA_SOURCE_OUTPUT_UNLINKED) { pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_STATE_CHANGED], o); if (PA_SOURCE_OUTPUT_IS_LINKED(state)) pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } pa_source_update_status(o->source); }
void pa_device_port_set_available(pa_device_port *p, pa_available_t status) { pa_core *core; pa_assert(p); if (p->available == status) return; /* pa_assert(status != PA_AVAILABLE_UNKNOWN); */ p->available = status; pa_log_debug("Setting port %s to status %s", p->name, status == PA_AVAILABLE_YES ? "yes" : status == PA_AVAILABLE_NO ? "no" : "unknown"); /* Post subscriptions to the card which owns us */ pa_assert_se(core = p->core); pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, p->card->index); pa_hook_fire(&core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED], p); }
static void pa_module_free(pa_module *m) { pa_assert(m); pa_assert(m->core); pa_log_info("Unloading \"%s\" (index: #%u).", m->name, m->index); if (m->done) m->done(m); if (m->proplist) pa_proplist_free(m->proplist); lt_dlclose(m->dl); pa_log_info("Unloaded \"%s\" (index: #%u).", m->name, m->index); pa_subscription_post(m->core, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_REMOVE, m->index); pa_xfree(m->name); pa_xfree(m->argument); pa_xfree(m); }
/* Called from main context */ void pa_source_output_set_name(pa_source_output *o, const char *name) { const char *old; pa_assert_ctl_context(); pa_source_output_assert_ref(o); if (!name && !pa_proplist_contains(o->proplist, PA_PROP_MEDIA_NAME)) return; old = pa_proplist_gets(o->proplist, PA_PROP_MEDIA_NAME); if (old && name && !strcmp(old, name)) return; if (name) pa_proplist_sets(o->proplist, PA_PROP_MEDIA_NAME, name); else pa_proplist_unset(o->proplist, PA_PROP_MEDIA_NAME); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state)) { pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PROPLIST_CHANGED], o); pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); } }
/* Called from main context */ void pa_source_output_put(pa_source_output *o) { pa_source_output_state_t state; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(o->state == PA_SOURCE_OUTPUT_INIT); /* The following fields must be initialized properly */ pa_assert(o->push); pa_assert(o->kill); state = o->flags & PA_SOURCE_OUTPUT_START_CORKED ? PA_SOURCE_OUTPUT_CORKED : PA_SOURCE_OUTPUT_RUNNING; update_n_corked(o, state); o->state = state; pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0); pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, o->index); pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_PUT], o); pa_source_update_status(o->source); }
/* Called from main context */ int pa_source_output_finish_move(pa_source_output *o, pa_source *dest, pa_bool_t save) { pa_resampler *new_resampler; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert(!o->source); pa_source_assert_ref(dest); if (!pa_source_output_may_move_to(o, dest)) return -1; if (o->thread_info.resampler && pa_sample_spec_equal(pa_resampler_input_sample_spec(o->thread_info.resampler), &dest->sample_spec) && pa_channel_map_equal(pa_resampler_input_channel_map(o->thread_info.resampler), &dest->channel_map)) /* Try to reuse the old resampler if possible */ new_resampler = o->thread_info.resampler; else if ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) || !pa_sample_spec_equal(&o->sample_spec, &dest->sample_spec) || !pa_channel_map_equal(&o->channel_map, &dest->channel_map)) { /* Okey, we need a new resampler for the new source */ if (!(new_resampler = pa_resampler_new( o->core->mempool, &dest->sample_spec, &dest->channel_map, &o->sample_spec, &o->channel_map, o->requested_resample_method, ((o->flags & PA_SOURCE_OUTPUT_VARIABLE_RATE) ? PA_RESAMPLER_VARIABLE_RATE : 0) | ((o->flags & PA_SOURCE_OUTPUT_NO_REMAP) ? PA_RESAMPLER_NO_REMAP : 0) | (o->core->disable_remixing || (o->flags & PA_SOURCE_OUTPUT_NO_REMIX) ? PA_RESAMPLER_NO_REMIX : 0)))) { pa_log_warn("Unsupported resampling operation."); return -PA_ERR_NOTSUPPORTED; } } else new_resampler = NULL; if (o->moving) o->moving(o, dest); o->source = dest; o->save_source = save; pa_idxset_put(o->source->outputs, pa_source_output_ref(o), NULL); if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) o->source->n_corked++; /* Replace resampler */ if (new_resampler != o->thread_info.resampler) { if (o->thread_info.resampler) pa_resampler_free(o->thread_info.resampler); o->thread_info.resampler = new_resampler; pa_memblockq_free(o->thread_info.delay_memblockq); 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); o->actual_resample_method = new_resampler ? pa_resampler_get_method(new_resampler) : PA_RESAMPLER_INVALID; } pa_source_update_status(dest); pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_ADD_OUTPUT, o, 0, NULL) == 0); pa_log_debug("Successfully moved source output %i to %s.", o->index, dest->name); /* Notify everyone */ pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_FINISH], o); pa_subscription_post(o->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE, o->index); return 0; }
pa_module* pa_module_load(pa_core *c, const char *name, const char *argument) { pa_module *m = NULL; pa_bool_t (*load_once)(void); const char* (*get_deprecated)(void); pa_modinfo *mi; pa_assert(c); pa_assert(name); if (c->disallow_module_loading) goto fail; m = pa_xnew(pa_module, 1); m->name = pa_xstrdup(name); m->argument = pa_xstrdup(argument); m->load_once = FALSE; m->proplist = pa_proplist_new(); if (!(m->dl = lt_dlopenext(name))) { pa_log("Failed to open module \"%s\": %s", name, lt_dlerror()); goto fail; } if ((load_once = (pa_bool_t (*)(void)) pa_load_sym(m->dl, name, PA_SYMBOL_LOAD_ONCE))) { m->load_once = load_once(); if (m->load_once) { pa_module *i; uint32_t idx; /* OK, the module only wants to be loaded once, let's make sure it is */ for (i = pa_idxset_first(c->modules, &idx); i; i = pa_idxset_next(c->modules, &idx)) { if (strcmp(name, i->name) == 0) { pa_log("Module \"%s\" should be loaded once at most. Refusing to load.", name); goto fail; } } } } if ((get_deprecated = (const char* (*) (void)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_DEPRECATE))) { const char *t; if ((t = get_deprecated())) pa_log_warn("%s is deprecated: %s", name, t); } if (!(m->init = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_INIT))) { pa_log("Failed to load module \"%s\": symbol \""PA_SYMBOL_INIT"\" not found.", name); goto fail; } m->done = (void (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_DONE); m->get_n_used = (int (*)(pa_module*_m)) pa_load_sym(m->dl, name, PA_SYMBOL_GET_N_USED); m->userdata = NULL; m->core = c; m->unload_requested = FALSE; if (m->init(m) < 0) { pa_log_error("Failed to load module \"%s\" (argument: \"%s\"): initialization failed.", name, argument ? argument : ""); goto fail; } pa_assert_se(pa_idxset_put(c->modules, m, &m->index) >= 0); pa_assert(m->index != PA_IDXSET_INVALID); pa_log_info("Loaded \"%s\" (index: #%u; argument: \"%s\").", m->name, m->index, m->argument ? m->argument : ""); pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_MODULE|PA_SUBSCRIPTION_EVENT_NEW, m->index); if ((mi = pa_modinfo_get_by_handle(m->dl, name))) { if (mi->author && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_AUTHOR)) pa_proplist_sets(m->proplist, PA_PROP_MODULE_AUTHOR, mi->author); if (mi->description && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_DESCRIPTION)) pa_proplist_sets(m->proplist, PA_PROP_MODULE_DESCRIPTION, mi->description); if (mi->version && !pa_proplist_contains(m->proplist, PA_PROP_MODULE_VERSION)) pa_proplist_sets(m->proplist, PA_PROP_MODULE_VERSION, mi->version); pa_modinfo_free(mi); } return m; fail: if (m) { if (m->proplist) pa_proplist_free(m->proplist); pa_xfree(m->argument); pa_xfree(m->name); if (m->dl) lt_dlclose(m->dl); pa_xfree(m); } return NULL; }
int pa_scache_play_item(pa_core *c, const char *name, pa_sink *sink, pa_volume_t volume, pa_proplist *p, uint32_t *sink_input_idx) { pa_scache_entry *e; pa_cvolume r; pa_proplist *merged; pa_bool_t pass_volume; pa_assert(c); pa_assert(name); pa_assert(sink); if (!(e = pa_namereg_get(c, name, PA_NAMEREG_SAMPLE))) return -1; merged = pa_proplist_new(); pa_proplist_sets(merged, PA_PROP_MEDIA_NAME, name); pa_proplist_sets(merged, PA_PROP_EVENT_ID, name); if (e->lazy && !e->memchunk.memblock) { pa_channel_map old_channel_map = e->channel_map; if (pa_sound_file_load(c->mempool, e->filename, &e->sample_spec, &e->channel_map, &e->memchunk, merged) < 0) goto fail; pa_subscription_post(c, PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE|PA_SUBSCRIPTION_EVENT_CHANGE, e->index); if (e->volume_is_set) { if (pa_cvolume_valid(&e->volume)) pa_cvolume_remap(&e->volume, &old_channel_map, &e->channel_map); else pa_cvolume_reset(&e->volume, e->sample_spec.channels); } } if (!e->memchunk.memblock) goto fail; pa_log_debug("Playing sample \"%s\" on \"%s\"", name, sink->name); pass_volume = TRUE; if (e->volume_is_set && volume != PA_VOLUME_INVALID) { pa_cvolume_set(&r, e->sample_spec.channels, volume); pa_sw_cvolume_multiply(&r, &r, &e->volume); } else if (e->volume_is_set) r = e->volume; else if (volume != PA_VOLUME_INVALID) pa_cvolume_set(&r, e->sample_spec.channels, volume); else pass_volume = FALSE; pa_proplist_update(merged, PA_UPDATE_REPLACE, e->proplist); if (p) pa_proplist_update(merged, PA_UPDATE_REPLACE, p); if (pa_play_memchunk(sink, &e->sample_spec, &e->channel_map, &e->memchunk, pass_volume ? &r : NULL, merged, PA_SINK_INPUT_NO_CREATE_ON_SUSPEND|PA_SINK_INPUT_KILL_ON_SUSPEND, sink_input_idx) < 0) goto fail; pa_proplist_free(merged); if (e->lazy) time(&e->last_used_time); return 0; fail: pa_proplist_free(merged); return -1; }