static void process_rewind(struct userdata *u, pa_usec_t now) { size_t rewind_nbytes, in_buffer; pa_usec_t delay; pa_assert(u); rewind_nbytes = u->sink->thread_info.rewind_nbytes; if (!PA_SINK_IS_OPENED(u->sink->thread_info.state) || rewind_nbytes <= 0) goto do_nothing; pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes); if (u->timestamp <= now) goto do_nothing; delay = u->timestamp - now; in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec); if (in_buffer <= 0) goto do_nothing; if (rewind_nbytes > in_buffer) rewind_nbytes = in_buffer; pa_sink_process_rewind(u->sink, rewind_nbytes); u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec); pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); return; do_nothing: pa_sink_process_rewind(u->sink, 0); }
static void process_rewind(struct userdata *u) { size_t rewind_nbytes; pa_assert(u); if (!PA_SINK_IS_OPENED(u->sink->thread_info.state)) { pa_sink_process_rewind(u->sink, 0); return; } rewind_nbytes = u->sink->thread_info.rewind_nbytes; if (rewind_nbytes > 0) { pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes); rewind_nbytes = PA_MIN(u->memchunk.length, rewind_nbytes); u->memchunk.length -= rewind_nbytes; if (u->memchunk.length <= 0 && u->memchunk.memblock) { pa_memblock_unref(u->memchunk.memblock); pa_memchunk_reset(&u->memchunk); } pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); } pa_sink_process_rewind(u->sink, rewind_nbytes); }
/* Called from I/O thread context */ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; size_t amount = 0; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); if (u->sink->thread_info.rewind_nbytes > 0) { size_t max_rewrite; max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq); amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite); u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { unsigned c; pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); pa_log_debug("Resetting plugin"); /* Reset the plugin */ if (u->descriptor->deactivate) for (c = 0; c < (u->channels / u->max_ladspaport_count); c++) u->descriptor->deactivate(u->handle[c]); if (u->descriptor->activate) for (c = 0; c < (u->channels / u->max_ladspaport_count); c++) u->descriptor->activate(u->handle[c]); } } pa_sink_process_rewind(u->sink, amount); pa_memblockq_rewind(u->memblockq, nbytes); }
static void thread_func(void *userdata) { struct userdata *u = userdata; pa_assert(u); pa_log_debug("Thread starting up"); if (u->core->realtime_scheduling) pa_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); for (;;) { int ret; if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) if (u->sink->thread_info.rewind_requested) pa_sink_process_rewind(u->sink, 0); if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) goto fail; if (ret == 0) goto finish; } fail: /* If this was no regular exit from the loop we have to continue * processing messages until we received PA_MESSAGE_SHUTDOWN */ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); finish: pa_log_debug("Thread shutting down"); }
/* Called from I/O thread context */ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; size_t amount = 0; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); if (u->sink->thread_info.rewind_nbytes > 0) { size_t max_rewrite; max_rewrite = nbytes * u->sink_fs / u->fs + pa_memblockq_get_length(u->memblockq); amount = PA_MIN(u->sink->thread_info.rewind_nbytes * u->sink_fs / u->fs, max_rewrite); u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, true); /* Reset the input buffer */ memset(u->input_buffer, 0, u->hrir_samples * u->sink_fs); u->input_buffer_offset = 0; } } pa_sink_process_rewind(u->sink, amount); pa_memblockq_rewind(u->memblockq, nbytes * u->sink_fs / u->fs); }
static void thread_func(void *userdata) { struct userdata *u = userdata; int ret; pa_usec_t now; pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); u->timestamp = pa_rtclock_now(); for (;;) { if (u->sink->thread_info.state == PA_SINK_RUNNING) { now = pa_rtclock_now(); if (u->sink->thread_info.rewind_requested) { if (u->sink->thread_info.rewind_nbytes > 0) { process_rewind(u, now); } else { pa_sink_process_rewind(u->sink, 0); } } if (u->timestamp <= now) { pa_log_debug("thread_func: calling process_render"); process_render(u, now); } pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp); } else { pa_rtpoll_set_timer_disabled(u->rtpoll); } if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) { goto fail; } if (ret == 0) { goto finish; } } fail: /* If this was no regular exit from the loop we have to continue * processing messages until we received PA_MESSAGE_SHUTDOWN */ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); finish: pa_log_debug("Thread shutting down"); }
static void thread_func(void *userdata) { struct userdata *u = userdata; pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); for(;;){ struct pollfd *pollfd; int ret; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); /* Render some data and write it to the fifo */ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { if (u->sink->thread_info.rewind_requested) pa_sink_process_rewind(u->sink, 0); if (pollfd->revents) { if (process_render(u) < 0) goto fail; pollfd->revents = 0; } } /* Hmm, nothing to do. Let's sleep */ pollfd->events = (short) (u->sink->thread_info.state == PA_SINK_RUNNING ? POLLOUT : 0); if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) goto fail; if (ret == 0) goto finish; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); if (pollfd->revents & ~POLLOUT) { pa_log("FIFO shutdown."); goto fail; } } fail: /* If this was no regular exit from the loop we have to continue * processing messages until we received PA_MESSAGE_SHUTDOWN */ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); pa_log_debug("Shutting down Xen..."); xen_cleanup(); finish: pa_log_debug("Thread shutting down"); }
/* Called from I/O thread context */ static void sink_request_rewind_cb(pa_sink *s) { struct userdata *u; pa_sink_assert_ref(s); pa_assert_se(u = s->userdata); /* Do nothing */ pa_sink_process_rewind(u->sink, 0); }
/* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; float *src, *dst; size_t fs; unsigned n, h, c; pa_memchunk tchunk; pa_sink_input_assert_ref(i); pa_assert(chunk); pa_assert_se(u = i->userdata); /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { pa_memchunk nchunk; pa_sink_render(u->sink, nbytes, &nchunk); pa_memblockq_push(u->memblockq, &nchunk); pa_memblock_unref(nchunk.memblock); } tchunk.length = PA_MIN(nbytes, tchunk.length); pa_assert(tchunk.length > 0); fs = pa_frame_size(&i->sample_spec); n = (unsigned) (PA_MIN(tchunk.length, u->block_size) / fs); pa_assert(n > 0); chunk->index = 0; chunk->length = n*fs; chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); pa_memblockq_drop(u->memblockq, chunk->length); src = (float*) ((uint8_t*) pa_memblock_acquire(tchunk.memblock) + tchunk.index); dst = (float*) pa_memblock_acquire(chunk->memblock); for (h = 0; h < (u->channels / u->max_ladspaport_count); h++) { for (c = 0; c < u->input_count; c++) pa_sample_clamp(PA_SAMPLE_FLOAT32NE, u->input[c], sizeof(float), src+ h*u->max_ladspaport_count + c, u->channels*sizeof(float), n); u->descriptor->run(u->handle[h], n); for (c = 0; c < u->output_count; c++) pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst + h*u->max_ladspaport_count + c, u->channels*sizeof(float), u->output[c], sizeof(float), n); } pa_memblock_release(tchunk.memblock); pa_memblock_release(chunk->memblock); pa_memblock_unref(tchunk.memblock); return 0; }
static void thread_func(void *userdata) { struct userdata *u = userdata; pa_assert(u); pa_assert(u->sink || u->source); pa_log_debug("Thread starting up"); if (u->core->realtime_scheduling) pa_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); for (;;) { int ret; pa_bool_t need_timer = FALSE; if (u->sink) { if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) pa_sink_process_rewind(u->sink, 0); if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { do_write(u); need_timer = TRUE; } } if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { do_read(u); need_timer = TRUE; } if (need_timer) pa_rtpoll_set_timer_relative(u->rtpoll, u->poll_timeout); else pa_rtpoll_set_timer_disabled(u->rtpoll); /* Hmm, nothing to do. Let's sleep */ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) goto fail; if (ret == 0) goto finish; } fail: /* If this was no regular exit from the loop we have to continue * processing messages until we received PA_MESSAGE_SHUTDOWN */ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); finish: pa_log_debug("Thread shutting down"); }
static void process_rewind(struct context *context, pa_usec_t now) { size_t rewind_nbytes, in_buffer; pa_usec_t delay; pa_assert(context); /* Figure out how much we shall rewind and reset the counter */ rewind_nbytes = context->sink->thread_info.rewind_nbytes; context->sink->thread_info.rewind_nbytes = 0; if (rewind_nbytes <= 0) goto DO_NOTHING; pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes); if (context->timestamp <= now) goto DO_NOTHING; delay = context->timestamp - now; in_buffer = pa_usec_to_bytes(delay, &context->sink->sample_spec); if (in_buffer <= 0) goto DO_NOTHING; if (rewind_nbytes > in_buffer) rewind_nbytes = in_buffer; pa_sink_process_rewind(context->sink, rewind_nbytes); context->timestamp -= pa_bytes_to_usec(rewind_nbytes, &context->sink->sample_spec); context->skip_bytes += rewind_nbytes; pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); return; DO_NOTHING: pa_sink_process_rewind(context->sink, 0); }
/* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; pa_sink_input_assert_ref(i); pa_assert(chunk); pa_assert_se(u = i->userdata); /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); pa_sink_render(u->sink, nbytes, chunk); return 0; }
/* Called from I/O thread context */ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { size_t amount = 0; struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); if (u->sink->thread_info.rewind_nbytes > 0) { amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes); u->sink->thread_info.rewind_nbytes = 0; } pa_sink_process_rewind(u->sink, amount); }
static void process_rewind(struct userdata *u, pa_usec_t now) { size_t rewind_nbytes, in_buffer; pa_usec_t delay; pa_assert(u); /* Figure out how much we shall rewind and reset the counter */ rewind_nbytes = u->sink->thread_info.rewind_nbytes; u->sink->thread_info.rewind_nbytes = 0; pa_assert(rewind_nbytes > 0); pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes); if (u->timestamp <= now) goto do_nothing; delay = u->timestamp - now; in_buffer = pa_usec_to_bytes(delay, &u->sink->sample_spec); if (in_buffer <= 0) goto do_nothing; if (rewind_nbytes > in_buffer) rewind_nbytes = in_buffer; pa_sink_process_rewind(u->sink, rewind_nbytes); u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec); u->skip_bytes += rewind_nbytes; pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes); return; do_nothing: pa_sink_process_rewind(u->sink, 0); }
/* Called from I/O thread context */ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { size_t amount = 0; struct userdata *u; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); /* If the sink is not yet linked, there is nothing to rewind */ if (!PA_SINK_IS_LINKED(u->sink->thread_info.state)) return; if (u->sink->thread_info.rewind_nbytes > 0) { amount = PA_MIN(u->sink->thread_info.rewind_nbytes, nbytes); u->sink->thread_info.rewind_nbytes = 0; } pa_sink_process_rewind(u->sink, amount); }
/* Called from I/O thread context */ static void sink_input_process_rewind_cb(pa_sink_input *i, size_t nbytes) { struct userdata *u; size_t amount = 0; pa_sink_input_assert_ref(i); pa_assert_se(u = i->userdata); if (u->sink->thread_info.rewind_nbytes > 0) { size_t max_rewrite; max_rewrite = nbytes + pa_memblockq_get_length(u->memblockq); amount = PA_MIN(u->sink->thread_info.rewind_nbytes, max_rewrite); u->sink->thread_info.rewind_nbytes = 0; if (amount > 0) { pa_memblockq_seek(u->memblockq, - (int64_t) amount, PA_SEEK_RELATIVE, TRUE); /* (5) PUT YOUR CODE HERE TO RESET YOUR FILTER */ } } pa_sink_process_rewind(u->sink, amount); pa_memblockq_rewind(u->memblockq, nbytes); }
static void thread_func(void *userdata) { struct userdata *u = userdata; int write_type = 0, read_type = 0; short revents = 0; pa_assert(u); pa_log_debug("Thread starting up"); if (u->core->realtime_scheduling) pa_make_realtime(u->core->realtime_priority); pa_thread_mq_install(&u->thread_mq); for (;;) { int ret; /* pa_log("loop"); */ if (PA_UNLIKELY(u->sink && u->sink->thread_info.rewind_requested)) pa_sink_process_rewind(u->sink, 0); /* Render some data and write it to the dsp */ if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state) && ((revents & POLLOUT) || u->use_mmap || u->use_getospace)) { if (u->use_mmap) { if ((ret = mmap_write(u)) < 0) goto fail; revents &= ~POLLOUT; if (ret > 0) continue; } else { ssize_t l; pa_bool_t loop = FALSE, work_done = FALSE; l = (ssize_t) u->out_fragment_size; if (u->use_getospace) { audio_buf_info info; if (ioctl(u->fd, SNDCTL_DSP_GETOSPACE, &info) < 0) { pa_log_info("Device doesn't support SNDCTL_DSP_GETOSPACE: %s", pa_cstrerror(errno)); u->use_getospace = FALSE; } else { l = info.bytes; /* We loop only if GETOSPACE worked and we * actually *know* that we can write more than * one fragment at a time */ loop = TRUE; } } /* Round down to multiples of the fragment size, * because OSS needs that (at least some versions * do) */ l = (l/(ssize_t) u->out_fragment_size) * (ssize_t) u->out_fragment_size; /* Hmm, so poll() signalled us that we can read * something, but GETOSPACE told us there was nothing? * Hmm, make the best of it, try to read some data, to * avoid spinning forever. */ if (l <= 0 && (revents & POLLOUT)) { l = (ssize_t) u->out_fragment_size; loop = FALSE; } while (l > 0) { void *p; ssize_t t; if (u->memchunk.length <= 0) pa_sink_render(u->sink, (size_t) l, &u->memchunk); pa_assert(u->memchunk.length > 0); p = pa_memblock_acquire(u->memchunk.memblock); t = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type); pa_memblock_release(u->memchunk.memblock); /* pa_log("wrote %i bytes of %u", t, l); */ pa_assert(t != 0); if (t < 0) { if (errno == EINTR) continue; else if (errno == EAGAIN) { pa_log_debug("EAGAIN"); revents &= ~POLLOUT; break; } else { pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno)); goto fail; } } else { u->memchunk.index += (size_t) t; u->memchunk.length -= (size_t) t; if (u->memchunk.length <= 0) { pa_memblock_unref(u->memchunk.memblock); pa_memchunk_reset(&u->memchunk); } l -= t; revents &= ~POLLOUT; work_done = TRUE; } if (!loop) break; } if (work_done) continue; } } /* Try to read some data and pass it on to the source driver. */ if (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state) && ((revents & POLLIN) || u->use_mmap || u->use_getispace)) { if (u->use_mmap) { if ((ret = mmap_read(u)) < 0) goto fail; revents &= ~POLLIN; if (ret > 0) continue; } else { void *p; ssize_t l; pa_memchunk memchunk; pa_bool_t loop = FALSE, work_done = FALSE; l = (ssize_t) u->in_fragment_size; if (u->use_getispace) { audio_buf_info info; if (ioctl(u->fd, SNDCTL_DSP_GETISPACE, &info) < 0) { pa_log_info("Device doesn't support SNDCTL_DSP_GETISPACE: %s", pa_cstrerror(errno)); u->use_getispace = FALSE; } else { l = info.bytes; loop = TRUE; } } l = (l/(ssize_t) u->in_fragment_size) * (ssize_t) u->in_fragment_size; if (l <= 0 && (revents & POLLIN)) { l = (ssize_t) u->in_fragment_size; loop = FALSE; } while (l > 0) { ssize_t t; size_t k; pa_assert(l > 0); memchunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); k = pa_memblock_get_length(memchunk.memblock); if (k > (size_t) l) k = (size_t) l; k = (k/u->frame_size)*u->frame_size; p = pa_memblock_acquire(memchunk.memblock); t = pa_read(u->fd, p, k, &read_type); pa_memblock_release(memchunk.memblock); pa_assert(t != 0); /* EOF cannot happen */ /* pa_log("read %i bytes of %u", t, l); */ if (t < 0) { pa_memblock_unref(memchunk.memblock); if (errno == EINTR) continue; else if (errno == EAGAIN) { pa_log_debug("EAGAIN"); revents &= ~POLLIN; break; } else { pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno)); goto fail; } } else { memchunk.index = 0; memchunk.length = (size_t) t; pa_source_post(u->source, &memchunk); pa_memblock_unref(memchunk.memblock); l -= t; revents &= ~POLLIN; work_done = TRUE; } if (!loop) break; } if (work_done) continue; } } /* pa_log("loop2 revents=%i", revents); */ if (u->rtpoll_item) { struct pollfd *pollfd; pa_assert(u->fd >= 0); pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); pollfd->events = (short) (((u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 0) | ((u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) ? POLLOUT : 0)); } /* Hmm, nothing to do. Let's sleep */ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) goto fail; if (ret == 0) goto finish; if (u->rtpoll_item) { struct pollfd *pollfd; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); if (pollfd->revents & ~(POLLOUT|POLLIN)) { pa_log("DSP shutdown."); goto fail; } revents = pollfd->revents; } else revents = 0; } fail: /* If this was no regular exit from the loop we have to continue * processing messages until we received PA_MESSAGE_SHUTDOWN */ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); finish: pa_log_debug("Thread shutting down"); }
static void process_rewind(struct example_sink_userdata *u) { pa_assert(u); pa_sink_process_rewind(u->sink, 0); }
static void thread_func(void *userdata) { struct userdata *u = userdata; char buf[2048]; // max ring buffer size pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); for (;;) { struct pollfd *play_pollfd; struct pollfd *rec_pollfd; int ret; play_pollfd = pa_rtpoll_item_get_pollfd(u->play_rtpoll_item, NULL); rec_pollfd = pa_rtpoll_item_get_pollfd(u->rec_rtpoll_item, NULL); if (play_pollfd->revents & POLLIN) { if (libvchan_wait(u->play_ctrl) < 0) goto fail; play_pollfd->revents = 0; } if (rec_pollfd->revents & POLLIN) { if (libvchan_wait(u->rec_ctrl) < 0) goto fail; rec_pollfd->revents = 0; } /* Render some data and write it to the fifo */ if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { if (u->sink->thread_info.rewind_requested) pa_sink_process_rewind(u->sink, 0); if (libvchan_buffer_space(u->play_ctrl)) { if (process_sink_render(u) < 0) goto fail; } } if (u->source->thread_info.state == PA_SOURCE_RUNNING) { while (libvchan_data_ready(u->rec_ctrl)) { if (process_source_data(u) < 0) goto fail; } } else { /* discard the data */ if (libvchan_data_ready(u->rec_ctrl)) if (libvchan_read(u->rec_ctrl, buf, sizeof(buf)) < 0) goto fail; } /* Hmm, nothing to do. Let's sleep */ play_pollfd->events = POLLIN; rec_pollfd->events = POLLIN; #if PA_CHECK_VERSION(6,0,0) if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) #else if ((ret = pa_rtpoll_run(u->rtpoll, true)) < 0) #endif goto fail; if (ret == 0) goto finish; } fail: /* If this was no regular exit from the loop we have to continue * processing messages until we received PA_MESSAGE_SHUTDOWN */ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); finish: pa_log_debug("Thread shutting down"); }
static void thread_func(void *userdata) { struct userdata *u = userdata; pa_proplist *proplist; pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(u->thread_mq); proplist = tunnel_new_proplist(u); u->context = pa_context_new_with_proplist(u->thread_mainloop_api, "PulseAudio", proplist); pa_proplist_free(proplist); if (!u->context) { pa_log("Failed to create libpulse context"); goto fail; } if (u->cookie_file && pa_context_load_cookie_from_file(u->context, u->cookie_file) != 0) { pa_log_error("Can not load cookie file!"); goto fail; } pa_context_set_state_callback(u->context, context_state_cb, u); if (pa_context_connect(u->context, u->remote_server, PA_CONTEXT_NOAUTOSPAWN, NULL) < 0) { pa_log("Failed to connect libpulse context"); goto fail; } for (;;) { int ret; if (pa_mainloop_iterate(u->thread_mainloop, 1, &ret) < 0) { if (ret == 0) goto finish; else goto fail; } if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) pa_sink_process_rewind(u->sink, 0); if (u->connected && pa_stream_get_state(u->stream) == PA_STREAM_READY && PA_SINK_IS_LINKED(u->sink->thread_info.state)) { size_t writable; writable = pa_stream_writable_size(u->stream); if (writable > 0) { if(u->transcode.encoding != -1) { pa_memchunk memchunk; const void *p; size_t nbBytes; unsigned char *cbits; pa_sink_render_full(u->sink, u->transcode.frame_size*u->transcode.channels*u->transcode.sample_size, &memchunk); pa_assert(memchunk.length > 0); pa_assert(memchunk.length >= u->transcode.frame_size*u->transcode.channels); pa_log_debug("received memchunk length: %zu bytes", memchunk.length ); /* we have new data to write */ p = pa_memblock_acquire(memchunk.memblock); nbBytes = pa_transcode_encode(&u->transcode, (uint8_t*) p + memchunk.index, &cbits); pa_log_debug("encoded length: %zu bytes", nbBytes); /* TODO: Use pa_stream_begin_write() to reduce copying. */ ret = pa_stream_write_compressed(u->stream, (uint8_t*) cbits, nbBytes, NULL, /**< A cleanup routine for the data or NULL to request an internal copy */ 0, /** offset */ PA_SEEK_RELATIVE, u->transcode.frame_size*u->transcode.channels*u->transcode.sample_size); pa_memblock_release(memchunk.memblock); pa_memblock_unref(memchunk.memblock); if(nbBytes > 0) free(cbits); if (ret != 0) { pa_log_error("Could not write data into the stream ... ret = %i", ret); u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP); } } else { pa_memchunk memchunk; const void *p; pa_sink_render_full(u->sink, writable, &memchunk); pa_assert(memchunk.length > 0); /* we have new data to write */ p = pa_memblock_acquire(memchunk.memblock); /* TODO: Use pa_stream_begin_write() to reduce copying. */ ret = pa_stream_write(u->stream, (uint8_t*) p + memchunk.index, memchunk.length, NULL, /**< A cleanup routine for the data or NULL to request an internal copy */ 0, /** offset */ PA_SEEK_RELATIVE); pa_memblock_release(memchunk.memblock); pa_memblock_unref(memchunk.memblock); if (ret != 0) { pa_log_error("Could not write data into the stream ... ret = %i", ret); u->thread_mainloop_api->quit(u->thread_mainloop_api, TUNNEL_THREAD_FAILED_MAINLOOP); } } } } } fail: pa_asyncmsgq_post(u->thread_mq->outq, PA_MSGOBJECT(u->module->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq->inq, PA_MESSAGE_SHUTDOWN); finish: if (u->stream) { pa_stream_disconnect(u->stream); pa_stream_unref(u->stream); u->stream = NULL; } if (u->context) { pa_context_disconnect(u->context); pa_context_unref(u->context); u->context = NULL; } pa_log_debug("Thread shutting down"); }
/* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; float *src, *dst; size_t fs; unsigned n, c; pa_memchunk tchunk; pa_usec_t current_latency PA_GCC_UNUSED; pa_sink_input_assert_ref(i); pa_assert(chunk); pa_assert_se(u = i->userdata); /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); /* (1) IF YOU NEED A FIXED BLOCK SIZE USE * pa_memblockq_peek_fixed_size() HERE INSTEAD. NOTE THAT FILTERS * WHICH CAN DEAL WITH DYNAMIC BLOCK SIZES ARE HIGHLY * PREFERRED. */ while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { pa_memchunk nchunk; pa_sink_render(u->sink, nbytes, &nchunk); pa_memblockq_push(u->memblockq, &nchunk); pa_memblock_unref(nchunk.memblock); } /* (2) IF YOU NEED A FIXED BLOCK SIZE, THIS NEXT LINE IS NOT * NECESSARY */ tchunk.length = PA_MIN(nbytes, tchunk.length); pa_assert(tchunk.length > 0); fs = pa_frame_size(&i->sample_spec); n = (unsigned) (tchunk.length / fs); pa_assert(n > 0); chunk->index = 0; chunk->length = n*fs; chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); pa_memblockq_drop(u->memblockq, chunk->length); src = pa_memblock_acquire_chunk(&tchunk); dst = pa_memblock_acquire(chunk->memblock); /* (3) PUT YOUR CODE HERE TO DO SOMETHING WITH THE DATA */ /* As an example, copy input to output */ for (c = 0; c < u->channels; c++) { pa_sample_clamp(PA_SAMPLE_FLOAT32NE, dst+c, u->channels * sizeof(float), src+c, u->channels * sizeof(float), n); } pa_memblock_release(tchunk.memblock); pa_memblock_release(chunk->memblock); pa_memblock_unref(tchunk.memblock); /* (4) IF YOU NEED THE LATENCY FOR SOMETHING ACQUIRE IT LIKE THIS: */ current_latency = /* Get the latency of the master sink */ pa_sink_get_latency_within_thread(i->sink) + /* Add the latency internal to our sink input on top */ pa_bytes_to_usec(pa_memblockq_get_length(i->thread_info.render_memblockq), &i->sink->sample_spec); return 0; }
static void thread_func(void *userdata) { struct userdata *u = userdata; int write_type = 0; pa_memchunk silence; uint32_t silence_overhead = 0; double silence_ratio = 0; pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); pa_smoother_set_time_offset(u->smoother, pa_rtclock_now()); /* Create a chunk of memory that is our encoded silence sample. */ pa_memchunk_reset(&silence); for (;;) { int ret; if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) pa_sink_process_rewind(u->sink, 0); if (u->rtpoll_item) { struct pollfd *pollfd; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); /* Render some data and write it to the fifo */ if (/*PA_SINK_IS_OPENED(u->sink->thread_info.state) && */pollfd->revents) { pa_usec_t usec; int64_t n; void *p; if (!silence.memblock) { pa_memchunk silence_tmp; pa_memchunk_reset(&silence_tmp); silence_tmp.memblock = pa_memblock_new(u->core->mempool, 4096); silence_tmp.length = 4096; p = pa_memblock_acquire(silence_tmp.memblock); memset(p, 0, 4096); pa_memblock_release(silence_tmp.memblock); pa_raop_client_encode_sample(u->raop, &silence_tmp, &silence); pa_assert(0 == silence_tmp.length); silence_overhead = silence_tmp.length - 4096; silence_ratio = silence_tmp.length / 4096; pa_memblock_unref(silence_tmp.memblock); } for (;;) { ssize_t l; if (u->encoded_memchunk.length <= 0) { if (u->encoded_memchunk.memblock) pa_memblock_unref(u->encoded_memchunk.memblock); if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) { size_t rl; /* We render real data */ if (u->raw_memchunk.length <= 0) { if (u->raw_memchunk.memblock) pa_memblock_unref(u->raw_memchunk.memblock); pa_memchunk_reset(&u->raw_memchunk); /* Grab unencoded data */ pa_sink_render(u->sink, u->block_size, &u->raw_memchunk); } pa_assert(u->raw_memchunk.length > 0); /* Encode it */ rl = u->raw_memchunk.length; u->encoding_overhead += u->next_encoding_overhead; pa_raop_client_encode_sample(u->raop, &u->raw_memchunk, &u->encoded_memchunk); u->next_encoding_overhead = (u->encoded_memchunk.length - (rl - u->raw_memchunk.length)); u->encoding_ratio = u->encoded_memchunk.length / (rl - u->raw_memchunk.length); } else { /* We render some silence into our memchunk */ memcpy(&u->encoded_memchunk, &silence, sizeof(pa_memchunk)); pa_memblock_ref(silence.memblock); /* Calculate/store some values to be used with the smoother */ u->next_encoding_overhead = silence_overhead; u->encoding_ratio = silence_ratio; } } pa_assert(u->encoded_memchunk.length > 0); p = pa_memblock_acquire(u->encoded_memchunk.memblock); l = pa_write(u->fd, (uint8_t*) p + u->encoded_memchunk.index, u->encoded_memchunk.length, &write_type); pa_memblock_release(u->encoded_memchunk.memblock); pa_assert(l != 0); if (l < 0) { if (errno == EINTR) continue; else if (errno == EAGAIN) { /* OK, we filled all socket buffers up * now. */ goto filled_up; } else { pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); goto fail; } } else { u->offset += l; u->encoded_memchunk.index += l; u->encoded_memchunk.length -= l; pollfd->revents = 0; if (u->encoded_memchunk.length > 0) { /* we've completely written the encoded data, so update our overhead */ u->encoding_overhead += u->next_encoding_overhead; /* OK, we wrote less that we asked for, * hence we can assume that the socket * buffers are full now */ goto filled_up; } } } filled_up: /* At this spot we know that the socket buffers are * fully filled up. This is the best time to estimate * the playback position of the server */ n = u->offset - u->encoding_overhead; #ifdef SIOCOUTQ { int l; if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0) n -= (l / u->encoding_ratio); } #endif usec = pa_bytes_to_usec(n, &u->sink->sample_spec); if (usec > u->latency) usec -= u->latency; else usec = 0; pa_smoother_put(u->smoother, pa_rtclock_now(), usec); } /* Hmm, nothing to do. Let's sleep */ pollfd->events = POLLOUT; /*PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0;*/ } if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) goto fail; if (ret == 0) goto finish; if (u->rtpoll_item) { struct pollfd* pollfd; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); if (pollfd->revents & ~POLLOUT) { if (u->sink->thread_info.state != PA_SINK_SUSPENDED) { pa_log("FIFO shutdown."); goto fail; } /* We expect this to happen on occasion if we are not sending data. It's perfectly natural and normal and natural */ if (u->rtpoll_item) pa_rtpoll_item_free(u->rtpoll_item); u->rtpoll_item = NULL; } } } fail: /* If this was no regular exit from the loop we have to continue * processing messages until we received PA_MESSAGE_SHUTDOWN */ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); finish: if (silence.memblock) pa_memblock_unref(silence.memblock); pa_log_debug("Thread shutting down"); }
/* Called from input thread context */ static void source_output_push_cb(pa_source_output *o, const pa_memchunk *chunk) { struct userdata *u; pa_source_output_assert_ref(o); pa_source_output_assert_io_context(o); pa_assert_se(u = o->userdata); if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(u->source_output))) { pa_log("push when no link?"); return; } /* PUT YOUR CODE HERE TO DO SOMETHING WITH THE SOURCE DATA */ /* if uplink sink exists, pull data from there; simplify by using same length as chunk provided by source */ if(u->sink && (pa_sink_get_state(u->sink) == PA_SINK_RUNNING)) { pa_memchunk tchunk; size_t nbytes = chunk->length; pa_mix_info streams[2]; pa_memchunk target_chunk; void *target; int ch; /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); /* get data from the sink */ while (pa_memblockq_peek(u->sink_memblockq, &tchunk) < 0) { pa_memchunk nchunk; /* make sure we get nbytes from the sink with render_full, otherwise we cannot mix with the uplink */ pa_sink_render_full(u->sink, nbytes, &nchunk); pa_memblockq_push(u->sink_memblockq, &nchunk); pa_memblock_unref(nchunk.memblock); } pa_assert(tchunk.length == chunk->length); /* move the read pointer for sink memblockq */ pa_memblockq_drop(u->sink_memblockq, tchunk.length); /* allocate target chunk */ /* this could probably be done in-place, but having chunk as both the input and output creates issues with reference counts */ target_chunk.index = 0; target_chunk.length = chunk->length; pa_assert(target_chunk.length == chunk->length); target_chunk.memblock = pa_memblock_new(o->source->core->mempool, target_chunk.length); pa_assert( target_chunk.memblock ); /* get target pointer */ target = (void*)((uint8_t*)pa_memblock_acquire(target_chunk.memblock) + target_chunk.index); /* set-up mixing structure volume was taken care of in sink and source already */ streams[0].chunk = *chunk; for(ch=0;ch<o->sample_spec.channels;ch++) streams[0].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */ streams[0].volume.channels = o->sample_spec.channels; streams[1].chunk = tchunk; for(ch=0;ch<o->sample_spec.channels;ch++) streams[1].volume.values[ch] = PA_VOLUME_NORM; /* FIXME */ streams[1].volume.channels = o->sample_spec.channels; /* do mixing */ pa_mix(streams, /* 2 streams to be mixed */ 2, target, /* put result in target chunk */ chunk->length, /* same length as input */ (const pa_sample_spec *)&o->sample_spec, /* same sample spec for input and output */ NULL, /* no volume information */ FALSE); /* no mute */ pa_memblock_release(target_chunk.memblock); pa_memblock_unref(tchunk.memblock); /* clean-up */ /* forward the data to the virtual source */ pa_source_post(u->source, &target_chunk); pa_memblock_unref(target_chunk.memblock); /* clean-up */ } else { /* forward the data to the virtual source */ pa_source_post(u->source, chunk); } }
static void thread_func(void *userdata) { struct userdata *u = userdata; int write_type = 0; pa_assert(u); pa_log_debug("Thread starting up"); pa_thread_mq_install(&u->thread_mq); pa_smoother_set_time_offset(u->smoother, pa_rtclock_now()); for (;;) { int ret; if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) if (u->sink->thread_info.rewind_requested) pa_sink_process_rewind(u->sink, 0); if (u->rtpoll_item) { struct pollfd *pollfd; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); /* Render some data and write it to the fifo */ if (PA_SINK_IS_OPENED(u->sink->thread_info.state) && pollfd->revents) { pa_usec_t usec; int64_t n; for (;;) { ssize_t l; void *p; if (u->memchunk.length <= 0) pa_sink_render(u->sink, u->block_size, &u->memchunk); pa_assert(u->memchunk.length > 0); p = pa_memblock_acquire(u->memchunk.memblock); l = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, u->memchunk.length, &write_type); pa_memblock_release(u->memchunk.memblock); pa_assert(l != 0); if (l < 0) { if (errno == EINTR) continue; else if (errno == EAGAIN) { /* OK, we filled all socket buffers up * now. */ goto filled_up; } else { pa_log("Failed to write data to FIFO: %s", pa_cstrerror(errno)); goto fail; } } else { u->offset += l; u->memchunk.index += (size_t) l; u->memchunk.length -= (size_t) l; if (u->memchunk.length <= 0) { pa_memblock_unref(u->memchunk.memblock); pa_memchunk_reset(&u->memchunk); } pollfd->revents = 0; if (u->memchunk.length > 0) /* OK, we wrote less that we asked for, * hence we can assume that the socket * buffers are full now */ goto filled_up; } } filled_up: /* At this spot we know that the socket buffers are * fully filled up. This is the best time to estimate * the playback position of the server */ n = u->offset; #ifdef SIOCOUTQ { int l; if (ioctl(u->fd, SIOCOUTQ, &l) >= 0 && l > 0) n -= l; } #endif usec = pa_bytes_to_usec((uint64_t) n, &u->sink->sample_spec); if (usec > u->latency) usec -= u->latency; else usec = 0; pa_smoother_put(u->smoother, pa_rtclock_now(), usec); } /* Hmm, nothing to do. Let's sleep */ pollfd->events = (short) (PA_SINK_IS_OPENED(u->sink->thread_info.state) ? POLLOUT : 0); } if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0) goto fail; if (ret == 0) goto finish; if (u->rtpoll_item) { struct pollfd* pollfd; pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); if (pollfd->revents & ~POLLOUT) { pa_log("FIFO shutdown."); goto fail; } } } fail: /* If this was no regular exit from the loop we have to continue * processing messages until we received PA_MESSAGE_SHUTDOWN */ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL); pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN); finish: pa_log_debug("Thread shutting down"); }
/* Called from I/O thread context */ static int sink_input_pop_cb(pa_sink_input *i, size_t nbytes, pa_memchunk *chunk) { struct userdata *u; float *src, *dst; unsigned n; pa_memchunk tchunk; unsigned j, k, l; float sum_right, sum_left; float current_sample; pa_sink_input_assert_ref(i); pa_assert(chunk); pa_assert_se(u = i->userdata); /* Hmm, process any rewind request that might be queued up */ pa_sink_process_rewind(u->sink, 0); while (pa_memblockq_peek(u->memblockq, &tchunk) < 0) { pa_memchunk nchunk; pa_sink_render(u->sink, nbytes * u->sink_fs / u->fs, &nchunk); pa_memblockq_push(u->memblockq, &nchunk); pa_memblock_unref(nchunk.memblock); } tchunk.length = PA_MIN(nbytes * u->sink_fs / u->fs, tchunk.length); pa_assert(tchunk.length > 0); n = (unsigned) (tchunk.length / u->sink_fs); pa_assert(n > 0); chunk->index = 0; chunk->length = n * u->fs; chunk->memblock = pa_memblock_new(i->sink->core->mempool, chunk->length); pa_memblockq_drop(u->memblockq, n * u->sink_fs); src = pa_memblock_acquire_chunk(&tchunk); dst = pa_memblock_acquire(chunk->memblock); for (l = 0; l < n; l++) { memcpy(((char*) u->input_buffer) + u->input_buffer_offset * u->sink_fs, ((char *) src) + l * u->sink_fs, u->sink_fs); sum_right = 0; sum_left = 0; /* fold the input buffer with the impulse response */ for (j = 0; j < u->hrir_samples; j++) { for (k = 0; k < u->channels; k++) { current_sample = u->input_buffer[((u->input_buffer_offset + j) % u->hrir_samples) * u->channels + k]; sum_left += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_left[k]]; sum_right += current_sample * u->hrir_data[j * u->hrir_channels + u->mapping_right[k]]; } } dst[2 * l] = PA_CLAMP_UNLIKELY(sum_left, -1.0f, 1.0f); dst[2 * l + 1] = PA_CLAMP_UNLIKELY(sum_right, -1.0f, 1.0f); u->input_buffer_offset--; if (u->input_buffer_offset < 0) u->input_buffer_offset += u->hrir_samples; } pa_memblock_release(tchunk.memblock); pa_memblock_release(chunk->memblock); pa_memblock_unref(tchunk.memblock); return 0; }