/* Called from main context */ static void adjust_rates(struct userdata *u) { size_t buffer, fs; uint32_t old_rate, base_rate, new_rate; pa_usec_t buffer_latency; pa_assert(u); pa_assert_ctl_context(); pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); buffer = u->latency_snapshot.sink_input_buffer + u->latency_snapshot.source_output_buffer; if (u->latency_snapshot.recv_counter <= u->latency_snapshot.send_counter) buffer += (size_t) (u->latency_snapshot.send_counter - u->latency_snapshot.recv_counter); else buffer += PA_CLIP_SUB(buffer, (size_t) (u->latency_snapshot.recv_counter - u->latency_snapshot.send_counter)); buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec); pa_log_debug("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms", (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC, (double) buffer_latency / PA_USEC_PER_MSEC, (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC, ((double) u->latency_snapshot.sink_latency + buffer_latency + u->latency_snapshot.source_latency) / PA_USEC_PER_MSEC); pa_log_debug("Should buffer %zu bytes, buffered at minimum %zu bytes", u->latency_snapshot.max_request*2, u->latency_snapshot.min_memblockq_length); fs = pa_frame_size(&u->sink_input->sample_spec); old_rate = u->sink_input->sample_spec.rate; base_rate = u->source_output->sample_spec.rate; if (u->latency_snapshot.min_memblockq_length < u->latency_snapshot.max_request*2) new_rate = base_rate - (((u->latency_snapshot.max_request*2 - u->latency_snapshot.min_memblockq_length) / fs) *PA_USEC_PER_SEC)/u->adjust_time; else new_rate = base_rate + (((u->latency_snapshot.min_memblockq_length - u->latency_snapshot.max_request*2) / fs) *PA_USEC_PER_SEC)/u->adjust_time; if (new_rate < (uint32_t) (base_rate*0.8) || new_rate > (uint32_t) (base_rate*1.25)) { pa_log_warn("Sample rates too different, not adjusting (%u vs. %u).", base_rate, new_rate); new_rate = base_rate; } else { if (base_rate < new_rate + 20 && new_rate < base_rate + 20) new_rate = base_rate; /* Do the adjustment in small steps; 2‰ can be considered inaudible */ if (new_rate < (uint32_t) (old_rate*0.998) || new_rate > (uint32_t) (old_rate*1.002)) { pa_log_info("New rate of %u Hz not within 2‰ of %u Hz, forcing smaller adjustment", new_rate, old_rate); new_rate = PA_CLAMP(new_rate, (uint32_t) (old_rate*0.998), (uint32_t) (old_rate*1.002)); } } pa_sink_input_set_rate(u->sink_input, new_rate); pa_log_debug("[%s] Updated sampling rate to %lu Hz.", u->sink_input->sink->name, (unsigned long) new_rate); pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); }
void pa__done(pa_module *m) { pa_assert(m); struct example_sink_userdata *u = m->userdata; if (!u) { return; } if (u->sink) { pa_sink_unlink(u->sink); } if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); } pa_thread_mq_done(&u->thread_mq); if (u->sink) { pa_sink_unref(u->sink); } if (u->rtpoll) { pa_rtpoll_free(u->rtpoll); } if (u->output_fd != -1) { close(u->output_fd); } pa_xfree(u); }
void pa__done(pa_module*m) { struct userdata *u; pa_assert(m); if (!(u = m->userdata)) return; if (u->client) jack_client_close(u->client); if (u->sink) pa_sink_unlink(u->sink); if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); } pa_thread_mq_done(&u->thread_mq); if (u->sink) pa_sink_unref(u->sink); if (u->rtpoll_item) pa_rtpoll_item_free(u->rtpoll_item); if (u->jack_msgq) pa_asyncmsgq_unref(u->jack_msgq); if (u->rtpoll) pa_rtpoll_free(u->rtpoll); pa_xfree(u); }
void pa__done(pa_module *module) { struct context *context; pa_assert(module); context = module->userdata; if (!context) return; if (context->sink) { pa_sink_unlink(context->sink); } if (context->thread) { pa_asyncmsgq_send(context->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(context->thread); } pa_thread_mq_done(&context->thread_mq); if (context->sink) { pa_sink_unref(context->sink); } if (context->rtpoll) { pa_rtpoll_free(context->rtpoll); } pa_xfree(context); }
int main(int argc, char *argv[]) { pa_asyncmsgq *q; pa_thread *t; pa_assert_se(q = pa_asyncmsgq_new(0)); pa_assert_se(t = pa_thread_new("test", the_thread, q)); pa_log_info("Operation A post"); pa_asyncmsgq_post(q, NULL, OPERATION_A, NULL, 0, NULL, NULL); pa_thread_yield(); pa_log_info("Operation B post"); pa_asyncmsgq_post(q, NULL, OPERATION_B, NULL, 0, NULL, NULL); pa_thread_yield(); pa_log_info("Operation C send"); pa_asyncmsgq_send(q, NULL, OPERATION_C, NULL, 0, NULL); pa_thread_yield(); pa_log_info("Quit post"); pa_asyncmsgq_post(q, NULL, QUIT, NULL, 0, NULL, NULL); pa_thread_free(t); pa_asyncmsgq_unref(q); return 0; }
/* Called from main context */ int pa_source_output_start_move(pa_source_output *o) { pa_source *origin; int r; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert(o->source); if (!pa_source_output_may_move(o)) return -PA_ERR_NOTSUPPORTED; if ((r = pa_hook_fire(&o->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_MOVE_START], o)) < 0) return r; origin = o->source; pa_idxset_remove_by_data(o->source->outputs, o, NULL); if (pa_source_output_get_state(o) == PA_SOURCE_OUTPUT_CORKED) pa_assert_se(origin->n_corked-- >= 1); pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o->source), PA_SOURCE_MESSAGE_REMOVE_OUTPUT, o, 0, NULL) == 0); pa_source_update_status(o->source); o->source = NULL; pa_source_output_unref(o); return 0; }
/* Called from main context */ static void time_callback(pa_mainloop_api *a, pa_time_event *e, const struct timeval *t, void *userdata) { struct userdata *u = userdata; pa_assert(u); pa_assert(a); pa_assert(u->time_event == e); /* Restart timer right away */ pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); /* Get sink and source latency snapshot */ pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); adjust_rates(u); }
void pa__done(pa_module*m) { struct userdata *u; pa_assert(m); if (!(u = m->userdata)) return; if (u->source) pa_source_unlink(u->source); if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); } pa_thread_mq_done(&u->thread_mq); if (u->source) pa_source_unref(u->source); if (u->memchunk.memblock) pa_memblock_unref(u->memchunk.memblock); if (u->rtpoll) pa_rtpoll_free(u->rtpoll); pa_xfree(u); }
/* Called from main context */ pa_usec_t pa_source_output_set_requested_latency(pa_source_output *o, pa_usec_t usec) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) { pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_SET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); return usec; } /* If this source output is not realized yet or is being moved, we * have to touch the thread info data directly */ if (o->source) { if (!(o->source->flags & PA_SOURCE_DYNAMIC_LATENCY)) usec = pa_source_get_fixed_latency(o->source); if (usec != (pa_usec_t) -1) { pa_usec_t min_latency, max_latency; pa_source_get_latency_range(o->source, &min_latency, &max_latency); usec = PA_CLAMP(usec, min_latency, max_latency); } } o->thread_info.requested_source_latency = usec; return usec; }
/* Called from main context */ static void adjust_rates(struct userdata *u) { size_t buffer, fs; uint32_t old_rate, base_rate, new_rate; pa_usec_t buffer_latency; pa_assert(u); pa_assert_ctl_context(); pa_asyncmsgq_send(u->source_output->source->asyncmsgq, PA_MSGOBJECT(u->source_output), SOURCE_OUTPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input), SINK_INPUT_MESSAGE_LATENCY_SNAPSHOT, NULL, 0, NULL); buffer = u->latency_snapshot.sink_input_buffer + u->latency_snapshot.source_output_buffer; if (u->latency_snapshot.recv_counter <= u->latency_snapshot.send_counter) buffer += (size_t) (u->latency_snapshot.send_counter - u->latency_snapshot.recv_counter); else buffer += PA_CLIP_SUB(buffer, (size_t) (u->latency_snapshot.recv_counter - u->latency_snapshot.send_counter)); buffer_latency = pa_bytes_to_usec(buffer, &u->sink_input->sample_spec); pa_log_info("Loopback overall latency is %0.2f ms + %0.2f ms + %0.2f ms = %0.2f ms", (double) u->latency_snapshot.sink_latency / PA_USEC_PER_MSEC, (double) buffer_latency / PA_USEC_PER_MSEC, (double) u->latency_snapshot.source_latency / PA_USEC_PER_MSEC, ((double) u->latency_snapshot.sink_latency + buffer_latency + u->latency_snapshot.source_latency) / PA_USEC_PER_MSEC); pa_log_info("Should buffer %zu bytes, buffered at minimum %zu bytes", u->latency_snapshot.max_request*2, u->latency_snapshot.min_memblockq_length); fs = pa_frame_size(&u->sink_input->sample_spec); old_rate = u->sink_input->sample_spec.rate; base_rate = u->source_output->sample_spec.rate; if (u->latency_snapshot.min_memblockq_length < u->latency_snapshot.max_request*2) new_rate = base_rate - (((u->latency_snapshot.max_request*2 - u->latency_snapshot.min_memblockq_length) / fs) *PA_USEC_PER_SEC)/u->adjust_time; else new_rate = base_rate + (((u->latency_snapshot.min_memblockq_length - u->latency_snapshot.max_request*2) / fs) *PA_USEC_PER_SEC)/u->adjust_time; pa_log_info("Old rate %lu Hz, new rate %lu Hz", (unsigned long) old_rate, (unsigned long) new_rate); pa_sink_input_set_rate(u->sink_input, new_rate); pa_core_rttime_restart(u->core, u->time_event, pa_rtclock_now() + u->adjust_time); }
void pa__done(pa_module *m) { struct userdata *u; pa_assert(m); if (!(u = m->userdata)) return; if (u->sig) { ioctl(u->fd, I_SETSIG, 0); pa_signal_free(u->sig); } if (u->sink) pa_sink_unlink(u->sink); if (u->source) pa_source_unlink(u->source); if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); } pa_thread_mq_done(&u->thread_mq); if (u->sink) pa_sink_unref(u->sink); if (u->source) pa_source_unref(u->source); if (u->memchunk.memblock) pa_memblock_unref(u->memchunk.memblock); if (u->rtpoll_item) pa_rtpoll_item_free(u->rtpoll_item); if (u->rtpoll) pa_rtpoll_free(u->rtpoll); if (u->fd >= 0) close(u->fd); if (u->smoother) pa_smoother_free(u->smoother); pa_xfree(u->device_name); pa_xfree(u); }
/* 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 */ pa_usec_t pa_source_output_get_requested_latency(pa_source_output *o) { pa_source_output_assert_ref(o); pa_assert_ctl_context(); if (PA_SOURCE_OUTPUT_IS_LINKED(o->state) && o->source) { pa_usec_t usec = 0; pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_REQUESTED_LATENCY, &usec, 0, NULL) == 0); return usec; } /* If this source output is not realized yet or is being moved, we * have to touch the thread info data directly */ return o->thread_info.requested_source_latency; }
static int jack_process(jack_nframes_t nframes, void *arg) { struct userdata *u = arg; unsigned c; jack_nframes_t frame_time; pa_assert(u); /* We just forward the request to our other RT thread */ for (c = 0; c < u->channels; c++) pa_assert_se(u->buffer[c] = jack_port_get_buffer(u->port[c], nframes)); frame_time = jack_frame_time(u->client); pa_assert_se(pa_asyncmsgq_send(u->jack_msgq, PA_MSGOBJECT(u->sink), SINK_MESSAGE_RENDER, &frame_time, nframes, NULL) == 0); return 0; }
void pa__done(pa_module*m) { struct userdata *u; pa_assert(m); if (!(u = m->userdata)) return; if (u->sink) pa_sink_unlink(u->sink); if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); } pa_thread_mq_done(&u->thread_mq); if (u->sink) pa_sink_unref(u->sink); if (u->rtpoll_item) pa_rtpoll_item_free(u->rtpoll_item); if (u->rtpoll) pa_rtpoll_free(u->rtpoll); if (u->raw_memchunk.memblock) pa_memblock_unref(u->raw_memchunk.memblock); if (u->encoded_memchunk.memblock) pa_memblock_unref(u->encoded_memchunk.memblock); if (u->raop) pa_raop_client_free(u->raop); pa_xfree(u->read_data); pa_xfree(u->write_data); if (u->smoother) pa_smoother_free(u->smoother); if (u->fd >= 0) pa_close(u->fd); pa_xfree(u); }
/* Called from main context */ pa_usec_t pa_source_output_get_latency(pa_source_output *o, pa_usec_t *source_latency) { pa_usec_t r[2] = { 0, 0 }; pa_source_output_assert_ref(o); pa_assert_ctl_context(); pa_assert(PA_SOURCE_OUTPUT_IS_LINKED(o->state)); pa_assert_se(pa_asyncmsgq_send(o->source->asyncmsgq, PA_MSGOBJECT(o), PA_SOURCE_OUTPUT_MESSAGE_GET_LATENCY, r, 0, NULL) == 0); if (o->get_latency) r[0] += o->get_latency(o); if (source_latency) *source_latency = r[1]; return r[0]; }
void pa__done(pa_module *m) { struct userdata *u; pa_assert(m); if (!(u = m->userdata)) return; if (u->sink) pa_sink_unlink(u->sink); if (u->thread) { pa_asyncmsgq_send(u->thread_mq->inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); } if (u->thread_mq) { pa_thread_mq_done(u->thread_mq); pa_xfree(u->thread_mq); } if (u->thread_mainloop) pa_mainloop_free(u->thread_mainloop); if (u->cookie_file) pa_xfree(u->cookie_file); if (u->remote_sink_name) pa_xfree(u->remote_sink_name); if (u->remote_server) pa_xfree(u->remote_server); if (u->sink) pa_sink_unref(u->sink); if(u->transcode.encoding != -1) pa_transcode_free(&u->transcode); pa_xfree(u); }
/* 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__done(pa_module *m) { struct userdata *u; pa_assert(m); if (!(u = m->userdata)) return; if (u->sink) pa_sink_unlink(u->sink); if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); } pa_thread_mq_done(&u->thread_mq); if (u->sink) pa_sink_unref(u->sink); if (u->memchunk.memblock) pa_memblock_unref(u->memchunk.memblock); if (u->rtpoll_item) pa_rtpoll_item_free(u->rtpoll_item); if (u->rtpoll) pa_rtpoll_free(u->rtpoll); if (u->filename) { unlink(u->filename); pa_xfree(u->filename); } if (u->fd >= 0) pa_assert_se(pa_close(u->fd) == 0); pa_xfree(u); }
/* 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); }
int pa__init(pa_module *m) { struct userdata *u = NULL; const char *p; int fd = -1; int buffer_size; int mode; int record = 1, playback = 1; pa_sample_spec ss; pa_channel_map map; pa_modargs *ma = NULL; char *t; struct pollfd *pollfd; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("failed to parse module arguments."); goto fail; } if (pa_modargs_get_value_boolean(ma, "record", &record) < 0 || pa_modargs_get_value_boolean(ma, "playback", &playback) < 0) { pa_log("record= and playback= expect numeric argument."); goto fail; } if (!playback && !record) { pa_log("neither playback nor record enabled for device."); goto fail; } mode = (playback&&record) ? O_RDWR : (playback ? O_WRONLY : (record ? O_RDONLY : 0)); buffer_size = 16384; if (pa_modargs_get_value_s32(ma, "buffer_size", &buffer_size) < 0) { pa_log("failed to parse buffer size argument"); goto fail; } ss = m->core->default_sample_spec; if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) { pa_log("failed to parse sample specification"); goto fail; } if ((fd = open(p = pa_modargs_get_value(ma, "device", DEFAULT_DEVICE), mode | O_NONBLOCK)) < 0) goto fail; pa_log_info("device opened in %s mode.", mode == O_WRONLY ? "O_WRONLY" : (mode == O_RDONLY ? "O_RDONLY" : "O_RDWR")); if (pa_solaris_auto_format(fd, mode, &ss) < 0) goto fail; if (pa_solaris_set_buffer(fd, buffer_size) < 0) goto fail; u = pa_xmalloc(sizeof(struct userdata)); u->core = m->core; u->fd = fd; pa_memchunk_reset(&u->memchunk); /* We use this to get a reasonable chunk size */ u->page_size = PA_PAGE_SIZE; u->frame_size = pa_frame_size(&ss); u->buffer_size = buffer_size; u->written_bytes = 0; u->read_bytes = 0; u->module = m; m->userdata = u; pa_thread_mq_init(&u->thread_mq, m->core->mainloop); u->rtpoll = pa_rtpoll_new(); pa_rtpoll_item_new_asyncmsgq(u->rtpoll, PA_RTPOLL_EARLY, u->thread_mq.inq); pa_rtpoll_set_timer_periodic(u->rtpoll, pa_bytes_to_usec(u->buffer_size / 10, &ss)); u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); pollfd->fd = fd; pollfd->events = 0; pollfd->revents = 0; if (mode != O_WRONLY) { u->source = pa_source_new(m->core, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map); pa_assert(u->source); u->source->userdata = u; u->source->parent.process_msg = source_process_msg; pa_source_set_module(u->source, m); pa_source_set_description(u->source, t = pa_sprintf_malloc("Solaris PCM on '%s'", p)); pa_xfree(t); pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); u->source->flags = PA_SOURCE_HARDWARE|PA_SOURCE_LATENCY|PA_SOURCE_HW_VOLUME_CTRL; u->source->refresh_volume = 1; } else u->source = NULL; if (mode != O_RDONLY) { u->sink = pa_sink_new(m->core, __FILE__, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME), 0, &ss, &map); pa_assert(u->sink); u->sink->userdata = u; u->sink->parent.process_msg = sink_process_msg; pa_sink_set_module(u->sink, m); pa_sink_set_description(u->sink, t = pa_sprintf_malloc("Solaris PCM on '%s'", p)); pa_xfree(t); pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); u->sink->flags = PA_SINK_HARDWARE|PA_SINK_LATENCY|PA_SINK_HW_VOLUME_CTRL; u->sink->refresh_volume = 1; u->sink->refresh_mute = 1; } else u->sink = NULL; pa_assert(u->source || u->sink); u->sig = pa_signal_new(SIGPOLL, sig_callback, u); pa_assert(u->sig); ioctl(u->fd, I_SETSIG, S_MSG); if (!(u->thread = pa_thread_new(thread_func, u))) { pa_log("Failed to create thread."); goto fail; } /* Read mixer settings */ if (u->source) pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->source), PA_SOURCE_MESSAGE_GET_VOLUME, &u->source->volume, 0, NULL); if (u->sink) { pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_VOLUME, &u->sink->volume, 0, NULL); pa_asyncmsgq_send(u->thread_mq.inq, PA_MSGOBJECT(u->sink), PA_SINK_MESSAGE_GET_MUTE, &u->sink->muted, 0, NULL); } if (u->sink) pa_sink_put(u->sink); if (u->source) pa_source_put(u->source); pa_modargs_free(ma); return 0; fail: if (u) pa__done(m); else if (fd >= 0) close(fd); if (ma) pa_modargs_free(ma); return -1; }
/* 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; }
int pa__init(pa_module * m) { struct userdata *u; pa_sample_spec ss; pa_channel_map map; pa_modargs *ma; struct pollfd *pollfd; pa_sink_new_data data_sink; pa_source_new_data data_source; pa_assert(m); pa_log("vchan module loading"); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments."); goto fail; } ss = m->core->default_sample_spec; map = m->core->default_channel_map; 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; } u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; m->userdata = u; pa_memchunk_reset(&u->memchunk_sink); pa_memchunk_reset(&u->memchunk_source); u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); if ((do_conn(u)) < 0) { pa_log("get_early_allocated_vchan: %s", pa_cstrerror(errno)); goto fail; } /* SINK preparation */ pa_sink_new_data_init(&data_sink); data_sink.driver = __FILE__; data_sink.module = m; pa_sink_new_data_set_name(&data_sink, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); pa_proplist_sets(data_sink.proplist, PA_PROP_DEVICE_STRING, DEFAULT_SINK_NAME); pa_proplist_setf(data_sink.proplist, PA_PROP_DEVICE_DESCRIPTION, "Qubes VCHAN sink"); pa_sink_new_data_set_sample_spec(&data_sink, &ss); pa_sink_new_data_set_channel_map(&data_sink, &map); if (pa_modargs_get_proplist (ma, "sink_properties", data_sink.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Invalid properties"); pa_sink_new_data_done(&data_sink); goto fail; } u->sink = pa_sink_new(m->core, &data_sink, PA_SINK_LATENCY); pa_sink_new_data_done(&data_sink); if (!u->sink) { pa_log("Failed to create sink."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); pa_sink_set_max_request(u->sink, VCHAN_BUF); pa_sink_set_fixed_latency(u->sink, pa_bytes_to_usec (VCHAN_BUF, &u->sink->sample_spec)); u->play_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); pollfd = pa_rtpoll_item_get_pollfd(u->play_rtpoll_item, NULL); pollfd->fd = libvchan_fd_for_select(u->play_ctrl); pollfd->events = POLLIN; pollfd->revents = 0; /* SOURCE preparation */ pa_source_new_data_init(&data_source); data_source.driver = __FILE__; data_source.module = m; pa_source_new_data_set_name(&data_source, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME)); pa_proplist_sets(data_source.proplist, PA_PROP_DEVICE_STRING, DEFAULT_SOURCE_NAME); pa_proplist_setf(data_source.proplist, PA_PROP_DEVICE_DESCRIPTION, "Qubes VCHAN source"); pa_source_new_data_set_sample_spec(&data_source, &ss); pa_source_new_data_set_channel_map(&data_source, &map); if (pa_modargs_get_proplist(ma, "source_properties", data_source.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Invalid properties"); pa_source_new_data_done(&data_source); goto fail; } u->source = pa_source_new(m->core, &data_source, PA_SOURCE_LATENCY); pa_source_new_data_done(&data_source); if (!u->source) { pa_log("Failed to create source."); goto fail; } u->source->parent.process_msg = source_process_msg; u->source->userdata = u; pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(PIPE_BUF, &u->source->sample_spec)); u->rec_rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1); pollfd = pa_rtpoll_item_get_pollfd(u->rec_rtpoll_item, NULL); pollfd->fd = libvchan_fd_for_select(u->rec_ctrl); pollfd->events = POLLIN; pollfd->revents = 0; #if PA_CHECK_VERSION(0,9,22) if (!(u->thread = pa_thread_new("vchan-sink", thread_func, u))) { #else if (!(u->thread = pa_thread_new(thread_func, u))) { #endif pa_log("Failed to create thread."); goto fail; } pa_sink_put(u->sink); pa_source_put(u->source); pa_modargs_free(ma); return 0; fail: if (ma) pa_modargs_free(ma); pa__done(m); return -1; } int pa__get_n_used(pa_module * m) { struct userdata *u; pa_assert(m); pa_assert_se(u = m->userdata); return pa_sink_linked_by(u->sink); } void pa__done(pa_module * m) { struct userdata *u; pa_assert(m); if (!(u = m->userdata)) return; if (u->sink) pa_sink_unlink(u->sink); if (u->source) pa_source_unlink(u->source); if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); } pa_thread_mq_done(&u->thread_mq); if (u->sink) pa_sink_unref(u->sink); if (u->source) pa_source_unref(u->source); if (u->memchunk_sink.memblock) pa_memblock_unref(u->memchunk_sink.memblock); if (u->memchunk_source.memblock) pa_memblock_unref(u->memchunk_source.memblock); if (u->play_rtpoll_item) pa_rtpoll_item_free(u->play_rtpoll_item); if (u->rec_rtpoll_item) pa_rtpoll_item_free(u->rec_rtpoll_item); if (u->rtpoll) pa_rtpoll_free(u->rtpoll); if (u->play_ctrl) libvchan_close(u->play_ctrl); if (u->rec_ctrl) libvchan_close(u->rec_ctrl); pa_xfree(u); }
int pa__init(pa_module*m) { struct userdata *u = NULL; pa_sample_spec ss; pa_channel_map map; pa_modargs *ma = NULL; pa_sink_new_data data; size_t nbytes; pa_assert(m); if (!(ma = pa_modargs_new(m->argument, valid_modargs))) { pa_log("Failed to parse module arguments."); goto fail; } ss = m->core->default_sample_spec; map = m->core->default_channel_map; 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; } m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); pa_sink_new_data_init(&data); data.driver = __FILE__; data.module = m; pa_sink_new_data_set_name(&data, pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME)); pa_sink_new_data_set_sample_spec(&data, &ss); pa_sink_new_data_set_channel_map(&data, &map); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "xrdp sink"); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract"); if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist, PA_UPDATE_REPLACE) < 0) { pa_log("Invalid properties"); pa_sink_new_data_done(&data); goto fail; } u->sink = pa_sink_new(m->core, &data, PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY); pa_sink_new_data_done(&data); if (!u->sink) { pa_log("Failed to create sink object."); goto fail; } u->sink->parent.process_msg = sink_process_msg; u->sink->update_requested_latency = sink_update_requested_latency_cb; u->sink->userdata = u; pa_sink_set_asyncmsgq(u->sink, u->thread_mq.inq); pa_sink_set_rtpoll(u->sink, u->rtpoll); u->block_usec = BLOCK_USEC; pa_log_debug("3 block_usec %d", u->block_usec); nbytes = pa_usec_to_bytes(u->block_usec, &u->sink->sample_spec); pa_sink_set_max_rewind(u->sink, nbytes); pa_sink_set_max_request(u->sink, nbytes); u->display_num = get_display_num_from_display(getenv("DISPLAY")); #if defined(PA_CHECK_VERSION) #if PA_CHECK_VERSION(0, 9, 22) if (!(u->thread = pa_thread_new("xrdp-sink", thread_func, u))) { #else if (!(u->thread = pa_thread_new(thread_func, u))) { #endif #else if (!(u->thread = pa_thread_new(thread_func, u))) { #endif pa_log("Failed to create thread."); goto fail; } pa_sink_put(u->sink); pa_modargs_free(ma); return 0; fail: if (ma) { pa_modargs_free(ma); } pa__done(m); return -1; } int pa__get_n_used(pa_module *m) { struct userdata *u; pa_assert(m); pa_assert_se(u = m->userdata); return pa_sink_linked_by(u->sink); } void pa__done(pa_module*m) { struct userdata *u; pa_assert(m); if (!(u = m->userdata)) { return; } if (u->sink) { pa_sink_unlink(u->sink); } if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); } pa_thread_mq_done(&u->thread_mq); if (u->sink) { pa_sink_unref(u->sink); } if (u->rtpoll) { pa_rtpoll_free(u->rtpoll); } pa_xfree(u); }
pa_source *pa_droid_source_new(pa_module *m, pa_modargs *ma, const char *driver, pa_droid_card_data *card_data, pa_droid_mapping *am, pa_card *card) { struct userdata *u = NULL; char *thread_name = NULL; pa_source_new_data data; const char *module_id = NULL; /* const char *tmp; */ uint32_t sample_rate; uint32_t alternate_sample_rate; audio_devices_t dev_in; pa_sample_spec sample_spec; pa_channel_map channel_map; bool namereg_fail = false; pa_droid_config_audio *config = NULL; /* Only used when source is created without card */ uint32_t source_buffer = 0; char audio_source[32]; int ret; audio_format_t hal_audio_format = 0; audio_channel_mask_t hal_channel_mask = 0; pa_assert(m); pa_assert(ma); pa_assert(driver); /* When running under card use hw module name for source by default. */ if (card && ma) module_id = am->input->module->name; else module_id = pa_modargs_get_value(ma, "module_id", DEFAULT_MODULE_ID); sample_spec = m->core->default_sample_spec; channel_map = m->core->default_channel_map; if (pa_modargs_get_sample_spec_and_channel_map(ma, &sample_spec, &channel_map, PA_CHANNEL_MAP_AIFF) < 0) { pa_log("Failed to parse sample specification and channel map."); goto fail; } alternate_sample_rate = m->core->alternate_sample_rate; if (pa_modargs_get_alternate_sample_rate(ma, &alternate_sample_rate) < 0) { pa_log("Failed to parse alternate sample rate."); goto fail; } if (pa_modargs_get_value_u32(ma, "source_buffer", &source_buffer) < 0) { pa_log("Failed to parse source_buffer. Needs to be integer >= 0."); goto fail; } u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; u->card = card; u->rtpoll = pa_rtpoll_new(); pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll); /* Enabled routing changes by default. */ u->routing_changes_enabled = true; if (card_data) { pa_assert(card); u->card_data = card_data; pa_assert_se((u->hw_module = pa_droid_hw_module_get(u->core, NULL, card_data->module_id))); } else { /* Stand-alone source */ if (!(config = pa_droid_config_load(ma))) goto fail; /* Ownership of config transfers to hw_module if opening of hw module succeeds. */ if (!(u->hw_module = pa_droid_hw_module_get(u->core, config, module_id))) goto fail; } if (!pa_convert_format(sample_spec.format, CONV_FROM_PA, &hal_audio_format)) { pa_log("Sample spec format %u not supported.", sample_spec.format); goto fail; } for (int i = 0; i < channel_map.channels; i++) { audio_channel_mask_t c; if (!pa_convert_input_channel(channel_map.map[i], CONV_FROM_PA, &c)) { pa_log("Failed to convert channel map."); goto fail; } hal_channel_mask |= c; } struct audio_config config_in = { .sample_rate = sample_spec.rate, .channel_mask = hal_channel_mask, .format = hal_audio_format }; /* Default routing */ /* FIXME So while setting routing through stream with HALv2 API fails, creation of stream * requires HALv2 style device to work properly. So until that oddity is resolved we always * set AUDIO_DEVICE_IN_BUILTIN_MIC as initial device here. */ #if 0 pa_assert_se(pa_string_convert_input_device_str_to_num("AUDIO_DEVICE_IN_BUILTIN_MIC", &dev_in)); if ((tmp = pa_modargs_get_value(ma, "input_devices", NULL))) { audio_devices_t tmp_dev; if (parse_device_list(tmp, &tmp_dev) && tmp_dev) dev_in = tmp_dev; pa_log_debug("Set initial devices %s", tmp); } #else pa_log_info("FIXME: Setting AUDIO_DEVICE_IN_BUILTIN_MIC as initial device."); dev_in = AUDIO_DEVICE_IN_BUILTIN_MIC; #endif pa_droid_hw_module_lock(u->hw_module); ret = u->hw_module->device->open_input_stream(u->hw_module->device, u->hw_module->stream_in_id, dev_in, &config_in, &u->stream); /* On some devices the first call will fail if the config parameters are * not supported, but it'll automatically set the right ones, expecting * the caller to call it again, so let's try at least one more time */ if (!u->stream) ret = u->hw_module->device->open_input_stream(u->hw_module->device, u->hw_module->stream_in_id, dev_in, &config_in, &u->stream); u->hw_module->stream_in_id++; pa_droid_hw_module_unlock(u->hw_module); if (ret < 0) { pa_log("Failed to open input stream."); goto fail; } if ((sample_rate = u->stream->common.get_sample_rate(&u->stream->common)) != sample_spec.rate) { pa_log_warn("Requested sample rate %u but got %u instead.", sample_spec.rate, sample_rate); sample_spec.rate = sample_rate; } u->buffer_size = u->stream->common.get_buffer_size(&u->stream->common); if (source_buffer) { if (source_buffer < u->buffer_size) pa_log_warn("Requested buffer size %u less than HAL reported buffer size (%u).", source_buffer, u->buffer_size); else if (source_buffer % u->buffer_size) { uint32_t trunc = (source_buffer / u->buffer_size) * u->buffer_size; pa_log_warn("Requested buffer size %u not multiple of HAL buffer size (%u). Using buffer size %u", source_buffer, u->buffer_size, trunc); u->buffer_size = trunc; } else { pa_log_info("Using requested buffer size %u.", source_buffer); u->buffer_size = source_buffer; } } pa_log_info("Created Android stream with device: %u sample rate: %u channel mask: %u format: %u buffer size: %u", dev_in, sample_rate, config_in.channel_mask, config_in.format, u->buffer_size); /* Setting audio source to MIC by default */ pa_snprintf(audio_source, sizeof(audio_source), "%s=%u", AUDIO_PARAMETER_STREAM_INPUT_SOURCE, AUDIO_SOURCE_MIC); u->stream->common.set_parameters(&u->stream->common, audio_source); pa_log_debug("Setting audio source to AUDIO_SOURCE_MIC by default"); pa_source_new_data_init(&data); data.driver = driver; data.module = m; data.card = card; source_set_name(ma, &data, module_id); /* We need to give pa_modargs_get_value_boolean() a pointer to a local * variable instead of using &data.namereg_fail directly, because * data.namereg_fail is a bitfield and taking the address of a bitfield * variable is impossible. */ namereg_fail = data.namereg_fail; if (pa_modargs_get_value_boolean(ma, "namereg_fail", &namereg_fail) < 0) { pa_log("Failed to parse namereg_fail argument."); pa_source_new_data_done(&data); goto fail; } data.namereg_fail = namereg_fail; pa_source_new_data_set_sample_spec(&data, &sample_spec); pa_source_new_data_set_channel_map(&data, &channel_map); pa_source_new_data_set_alternate_sample_rate(&data, alternate_sample_rate); if (am) pa_droid_add_ports(data.ports, am, card); u->source = pa_source_new(m->core, &data, PA_SOURCE_HARDWARE); pa_source_new_data_done(&data); if (!u->source) { pa_log("Failed to create source."); goto fail; } u->source->userdata = u; u->source->parent.process_msg = source_process_msg; source_set_mute_control(u); u->source->set_port = source_set_port_cb; pa_source_set_asyncmsgq(u->source, u->thread_mq.inq); pa_source_set_rtpoll(u->source, u->rtpoll); /* Disable rewind for droid source */ pa_source_set_max_rewind(u->source, 0); thread_name = pa_sprintf_malloc("droid-source-%s", module_id); if (!(u->thread = pa_thread_new(thread_name, thread_func, u))) { pa_log("Failed to create thread."); goto fail; } pa_xfree(thread_name); thread_name = NULL; pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(u->buffer_size, &sample_spec)); pa_log_debug("Set fixed latency %" PRIu64 " usec", pa_bytes_to_usec(u->buffer_size, &sample_spec)); if (u->source->active_port) source_set_port_cb(u->source, u->source->active_port); pa_source_put(u->source); return u->source; fail: pa_xfree(thread_name); if (config) pa_xfree(config); if (u) userdata_free(u); return NULL; } void pa_droid_source_free(pa_source *s) { struct userdata *u; pa_source_assert_ref(s); pa_assert_se(u = s->userdata); userdata_free(u); } static void userdata_free(struct userdata *u) { if (u->source) pa_source_unlink(u->source); if (u->thread) { pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL); pa_thread_free(u->thread); } pa_thread_mq_done(&u->thread_mq); if (u->source) pa_source_unref(u->source); if (u->memchunk.memblock) pa_memblock_unref(u->memchunk.memblock); if (u->hw_module && u->stream) { pa_droid_hw_module_lock(u->hw_module); u->hw_module->device->close_input_stream(u->hw_module->device, u->stream); pa_droid_hw_module_unlock(u->hw_module); } // Stand alone source if (u->hw_module) pa_droid_hw_module_unref(u->hw_module); pa_xfree(u); }
int pa_sink_input_ext_set_volume_limit(struct pa_sink_input *sinp, pa_volume_t limit) { pa_sink *sink; int retval; uint64_t limit64; pa_volume_t value; pa_cvolume *factor; pa_cvolume *real; int changed; int i; pa_assert(sinp); pa_assert_se((sink = sinp->sink)); retval = 0; if (limit == 0) pa_sink_input_set_mute(sinp, TRUE, TRUE); else { pa_sink_input_set_mute(sinp, FALSE, TRUE); if (limit > PA_VOLUME_NORM) limit = PA_VOLUME_NORM; factor = &sinp->volume_factor; real = &sinp->real_ratio; limit64 = (uint64_t)limit * (uint64_t)PA_VOLUME_NORM; changed = FALSE; if (real->channels != factor->channels) { pa_log_debug("channel number mismatch"); retval = -1; } else { for (i = 0; i < factor->channels; i++) { if (limit < real->values[i]) value = limit64 / (uint64_t)real->values[i]; else value = PA_VOLUME_NORM; if (value != factor->values[i]) { changed = 1; factor->values[i] = value; } } if (changed) { if (pa_sink_flat_volume_enabled(sink)) retval = 1; else { pa_sw_cvolume_multiply(&sinp->soft_volume, real, factor); pa_asyncmsgq_send(sink->asyncmsgq, PA_MSGOBJECT(sinp), PA_SINK_INPUT_MESSAGE_SET_SOFT_VOLUME, NULL, 0, NULL); } } } } return retval; }