pa_smoother* pa_smoother_new( uint64_t adjust_time, uint64_t history_time, bool monotonic, bool smoothing, unsigned min_history, uint64_t time_offset, bool paused) { pa_smoother *s; pa_assert(adjust_time > 0); pa_assert(history_time > 0); pa_assert(min_history >= 2); pa_assert(min_history <= HISTORY_MAX); s = pa_xnew(pa_smoother, 1); s->adjust_time = adjust_time; s->history_time = history_time; s->min_history = min_history; s->monotonic = monotonic; s->smoothing = smoothing; pa_smoother_reset(s, time_offset, paused); return s; }
static void thread_func(void *userdata) { struct userdata *u = userdata; unsigned short revents = 0; int ret, err; audio_info_t info; 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); pa_smoother_set_time_offset(u->smoother, pa_rtclock_now()); for (;;) { /* Render some data and write it to the dsp */ if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) process_rewind(u); if (u->sink && PA_SINK_IS_OPENED(u->sink->thread_info.state)) { pa_usec_t xtime0, ysleep_interval, xsleep_interval; uint64_t buffered_bytes; err = ioctl(u->fd, AUDIO_GETINFO, &info); if (err < 0) { pa_log("AUDIO_GETINFO ioctl failed: %s", pa_cstrerror(errno)); goto fail; } if (info.play.error) { pa_log_debug("buffer under-run!"); AUDIO_INITINFO(&info); info.play.error = 0; if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); pa_smoother_reset(u->smoother, pa_rtclock_now(), true); } for (;;) { void *p; ssize_t w; size_t len; int write_type = 1; /* * Since we cannot modify the size of the output buffer we fake it * by not filling it more than u->buffer_size. */ xtime0 = pa_rtclock_now(); buffered_bytes = get_playback_buffered_bytes(u); if (buffered_bytes >= (uint64_t)u->buffer_size) break; len = u->buffer_size - buffered_bytes; len -= len % u->frame_size; if (len < (size_t) u->minimum_request) break; if (!u->memchunk.length) pa_sink_render(u->sink, u->sink->thread_info.max_request, &u->memchunk); len = PA_MIN(u->memchunk.length, len); p = pa_memblock_acquire(u->memchunk.memblock); w = pa_write(u->fd, (uint8_t*) p + u->memchunk.index, len, &write_type); pa_memblock_release(u->memchunk.memblock); if (w <= 0) { if (errno == EINTR) { continue; } else if (errno == EAGAIN) { /* We may have realtime priority so yield the CPU to ensure that fd can become writable again. */ pa_log_debug("EAGAIN with %llu bytes buffered.", buffered_bytes); break; } else { pa_log("Failed to write data to DSP: %s", pa_cstrerror(errno)); goto fail; } } else { pa_assert(w % u->frame_size == 0); u->written_bytes += w; u->memchunk.index += w; u->memchunk.length -= w; if (u->memchunk.length <= 0) { pa_memblock_unref(u->memchunk.memblock); pa_memchunk_reset(&u->memchunk); } } } ysleep_interval = pa_bytes_to_usec(buffered_bytes / 2, &u->sink->sample_spec); xsleep_interval = pa_smoother_translate(u->smoother, xtime0, ysleep_interval); pa_rtpoll_set_timer_absolute(u->rtpoll, xtime0 + PA_MIN(xsleep_interval, ysleep_interval)); } else pa_rtpoll_set_timer_disabled(u->rtpoll); /* 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)) { pa_memchunk memchunk; void *p; ssize_t r; size_t len; err = ioctl(u->fd, AUDIO_GETINFO, &info); pa_assert(err >= 0); if (info.record.error) { pa_log_debug("buffer overflow!"); AUDIO_INITINFO(&info); info.record.error = 0; if (ioctl(u->fd, AUDIO_SETINFO, &info) < 0) pa_log("AUDIO_SETINFO: %s", pa_cstrerror(errno)); } err = ioctl(u->fd, I_NREAD, &len); pa_assert(err >= 0); if (len > 0) { memchunk.memblock = pa_memblock_new(u->core->mempool, len); pa_assert(memchunk.memblock); p = pa_memblock_acquire(memchunk.memblock); r = pa_read(u->fd, p, len, NULL); pa_memblock_release(memchunk.memblock); if (r < 0) { pa_memblock_unref(memchunk.memblock); if (errno == EAGAIN) break; else { pa_log("Failed to read data from DSP: %s", pa_cstrerror(errno)); goto fail; } } else { u->read_bytes += r; memchunk.index = 0; memchunk.length = r; pa_source_post(u->source, &memchunk); pa_memblock_unref(memchunk.memblock); revents &= ~POLLIN; } } } if (u->rtpoll_item) { struct pollfd *pollfd; pa_assert(u->fd >= 0); pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL); pollfd->events = (u->source && PA_SOURCE_IS_OPENED(u->source->thread_info.state)) ? POLLIN : 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: /* We have to continue processing messages until we receive the * SHUTDOWN message */ 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"); }