diff options
Diffstat (limited to 'src/modules/alsa/alsa-source.c')
-rw-r--r-- | src/modules/alsa/alsa-source.c | 306 |
1 files changed, 219 insertions, 87 deletions
diff --git a/src/modules/alsa/alsa-source.c b/src/modules/alsa/alsa-source.c index a6760e1e..fa3ac0aa 100644 --- a/src/modules/alsa/alsa-source.c +++ b/src/modules/alsa/alsa-source.c @@ -59,11 +59,24 @@ /* #define DEBUG_TIMING */ #define DEFAULT_DEVICE "default" -#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */ -#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ -#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ -#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ -#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */ + +#define DEFAULT_TSCHED_BUFFER_USEC (2*PA_USEC_PER_SEC) /* 2s */ +#define DEFAULT_TSCHED_WATERMARK_USEC (20*PA_USEC_PER_MSEC) /* 20ms */ + +#define TSCHED_WATERMARK_INC_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ +#define TSCHED_WATERMARK_DEC_STEP_USEC (5*PA_USEC_PER_MSEC) /* 5ms */ +#define TSCHED_WATERMARK_VERIFY_AFTER_USEC (20*PA_USEC_PER_SEC) /* 20s */ +#define TSCHED_WATERMARK_INC_THRESHOLD_USEC (0*PA_USEC_PER_MSEC) /* 0ms */ +#define TSCHED_WATERMARK_DEC_THRESHOLD_USEC (100*PA_USEC_PER_MSEC) /* 100ms */ +#define TSCHED_WATERMARK_STEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ + +#define TSCHED_MIN_SLEEP_USEC (10*PA_USEC_PER_MSEC) /* 10ms */ +#define TSCHED_MIN_WAKEUP_USEC (4*PA_USEC_PER_MSEC) /* 4ms */ + +#define SMOOTHER_MIN_INTERVAL (2*PA_USEC_PER_MSEC) /* 2ms */ +#define SMOOTHER_MAX_INTERVAL (200*PA_USEC_PER_MSEC) /* 200ms */ + +#define VOLUME_ACCURACY (PA_VOLUME_NORM/100) struct userdata { pa_core *core; @@ -91,9 +104,12 @@ struct userdata { hwbuf_unused, min_sleep, min_wakeup, - watermark_step; + watermark_inc_step, + watermark_dec_step, + watermark_inc_threshold, + watermark_dec_threshold; - unsigned nfragments; + pa_usec_t watermark_dec_not_before; char *device_name; char *control_device; @@ -106,6 +122,8 @@ struct userdata { pa_smoother *smoother; uint64_t read_count; + pa_usec_t smoother_interval; + pa_usec_t last_smoother_update; pa_reserve_wrapper *reserve; pa_hook_slot *reserve_slot; @@ -234,6 +252,7 @@ static int reserve_monitor_init(struct userdata *u, const char *dname) { static void fix_min_sleep_wakeup(struct userdata *u) { size_t max_use, max_use_2; pa_assert(u); + pa_assert(u->use_tsched); max_use = u->hwbuf_size - u->hwbuf_unused; max_use_2 = pa_frame_align(max_use/2, &u->source->sample_spec); @@ -248,6 +267,7 @@ static void fix_min_sleep_wakeup(struct userdata *u) { static void fix_tsched_watermark(struct userdata *u) { size_t max_use; pa_assert(u); + pa_assert(u->use_tsched); max_use = u->hwbuf_size - u->hwbuf_unused; @@ -258,7 +278,7 @@ static void fix_tsched_watermark(struct userdata *u) { u->tsched_watermark = u->min_wakeup; } -static void adjust_after_overrun(struct userdata *u) { +static void increase_watermark(struct userdata *u) { size_t old_watermark; pa_usec_t old_min_latency, new_min_latency; @@ -267,36 +287,72 @@ static void adjust_after_overrun(struct userdata *u) { /* First, just try to increase the watermark */ old_watermark = u->tsched_watermark; - u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_step); - + u->tsched_watermark = PA_MIN(u->tsched_watermark * 2, u->tsched_watermark + u->watermark_inc_step); fix_tsched_watermark(u); if (old_watermark != u->tsched_watermark) { - pa_log_notice("Increasing wakeup watermark to %0.2f ms", - (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); + pa_log_info("Increasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); return; } /* Hmm, we cannot increase the watermark any further, hence let's raise the latency */ old_min_latency = u->source->thread_info.min_latency; - new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_STEP_USEC); + new_min_latency = PA_MIN(old_min_latency * 2, old_min_latency + TSCHED_WATERMARK_INC_STEP_USEC); new_min_latency = PA_MIN(new_min_latency, u->source->thread_info.max_latency); if (old_min_latency != new_min_latency) { - pa_log_notice("Increasing minimal latency to %0.2f ms", - (double) new_min_latency / PA_USEC_PER_MSEC); + pa_log_info("Increasing minimal latency to %0.2f ms", + (double) new_min_latency / PA_USEC_PER_MSEC); pa_source_set_latency_range_within_thread(u->source, new_min_latency, u->source->thread_info.max_latency); - return; } /* When we reach this we're officialy fucked! */ } +static void decrease_watermark(struct userdata *u) { + size_t old_watermark; + pa_usec_t now; + + pa_assert(u); + pa_assert(u->use_tsched); + + now = pa_rtclock_now(); + + if (u->watermark_dec_not_before <= 0) + goto restart; + + if (u->watermark_dec_not_before > now) + return; + + old_watermark = u->tsched_watermark; + + if (u->tsched_watermark < u->watermark_dec_step) + u->tsched_watermark = u->tsched_watermark / 2; + else + u->tsched_watermark = PA_MAX(u->tsched_watermark / 2, u->tsched_watermark - u->watermark_dec_step); + + fix_tsched_watermark(u); + + if (old_watermark != u->tsched_watermark) + pa_log_info("Decreasing wakeup watermark to %0.2f ms", + (double) pa_bytes_to_usec(u->tsched_watermark, &u->source->sample_spec) / PA_USEC_PER_MSEC); + + /* We don't change the latency range*/ + +restart: + u->watermark_dec_not_before = now + TSCHED_WATERMARK_VERIFY_AFTER_USEC; +} + static pa_usec_t hw_sleep_time(struct userdata *u, pa_usec_t *sleep_usec, pa_usec_t*process_usec) { pa_usec_t wm, usec; + pa_assert(sleep_usec); + pa_assert(process_usec); + pa_assert(u); + pa_assert(u->use_tsched); usec = pa_source_get_requested_latency_within_thread(u->source); @@ -333,6 +389,9 @@ static int try_recover(struct userdata *u, const char *call, int err) { if (err == -EPIPE) pa_log_debug("%s: Buffer overrun!", call); + if (err == -ESTRPIPE) + pa_log_debug("%s: System suspended!", call); + if ((err = snd_pcm_recover(u->pcm_handle, err, 1)) < 0) { pa_log("%s: %s", call, pa_alsa_strerror(err)); return -1; @@ -342,24 +401,23 @@ static int try_recover(struct userdata *u, const char *call, int err) { return 0; } -static size_t check_left_to_record(struct userdata *u, size_t n_bytes) { +static size_t check_left_to_record(struct userdata *u, size_t n_bytes, pa_bool_t on_timeout) { size_t left_to_record; size_t rec_space = u->hwbuf_size - u->hwbuf_unused; + pa_bool_t overrun = FALSE; /* We use <= instead of < for this check here because an overrun * only happens after the last sample was processed, not already when * it is removed from the buffer. This is particularly important * when block transfer is used. */ - if (n_bytes <= rec_space) { + if (n_bytes <= rec_space) left_to_record = rec_space - n_bytes; + else { -#ifdef DEBUG_TIMING - pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); -#endif - - } else { + /* We got a dropout. What a mess! */ left_to_record = 0; + overrun = TRUE; #ifdef DEBUG_TIMING PA_DEBUG_TRAP; @@ -367,15 +425,36 @@ static size_t check_left_to_record(struct userdata *u, size_t n_bytes) { if (pa_log_ratelimit()) pa_log_info("Overrun!"); + } - if (u->use_tsched) - adjust_after_overrun(u); +#ifdef DEBUG_TIMING + pa_log_debug("%0.2f ms left to record", (double) pa_bytes_to_usec(left_to_record, &u->source->sample_spec) / PA_USEC_PER_MSEC); +#endif + + if (u->use_tsched) { + pa_bool_t reset_not_before = TRUE; + + if (overrun || left_to_record < u->watermark_inc_threshold) + increase_watermark(u); + else if (left_to_record > u->watermark_dec_threshold) { + reset_not_before = FALSE; + + /* We decrease the watermark only if have actually been + * woken up by a timeout. If something else woke us up + * it's too easy to fulfill the deadlines... */ + + if (on_timeout) + decrease_watermark(u); + } + + if (reset_not_before) + u->watermark_dec_not_before = 0; } return left_to_record; } -static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) { pa_bool_t work_done = FALSE; pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_record; @@ -391,6 +470,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled snd_pcm_sframes_t n; size_t n_bytes; int r; + pa_bool_t after_avail = TRUE; if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) { @@ -406,7 +486,8 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled pa_log_debug("avail: %lu", (unsigned long) n_bytes); #endif - left_to_record = check_left_to_record(u, n_bytes); + left_to_record = check_left_to_record(u, n_bytes, on_timeout); + on_timeout = FALSE; if (u->use_tsched) if (!polled && @@ -463,6 +544,9 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled if (PA_UNLIKELY((err = pa_alsa_safe_mmap_begin(u->pcm_handle, &areas, &offset, &frames, u->hwbuf_size, &u->source->sample_spec)) < 0)) { + if (!after_avail && err == -EAGAIN) + break; + if ((r = try_recover(u, "snd_pcm_mmap_begin", err)) == 0) continue; @@ -473,6 +557,12 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled if (frames > pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size) frames = pa_mempool_block_size_max(u->source->core->mempool)/u->frame_size; + if (!after_avail && frames == 0) + break; + + pa_assert(frames > 0); + after_avail = FALSE; + /* Check these are multiples of 8 bit */ pa_assert((areas[0].first & 7) == 0); pa_assert((areas[0].step & 7)== 0); @@ -523,7 +613,7 @@ static int mmap_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled return work_done ? 1 : 0; } -static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled) { +static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled, pa_bool_t on_timeout) { int work_done = FALSE; pa_usec_t max_sleep_usec = 0, process_usec = 0; size_t left_to_record; @@ -539,6 +629,7 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled snd_pcm_sframes_t n; size_t n_bytes; int r; + pa_bool_t after_avail = TRUE; if (PA_UNLIKELY((n = pa_alsa_safe_avail(u->pcm_handle, u->hwbuf_size, &u->source->sample_spec)) < 0)) { @@ -549,7 +640,8 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled } n_bytes = (size_t) n * u->frame_size; - left_to_record = check_left_to_record(u, n_bytes); + left_to_record = check_left_to_record(u, n_bytes, on_timeout); + on_timeout = FALSE; if (u->use_tsched) if (!polled && @@ -599,17 +691,26 @@ static int unix_read(struct userdata *u, pa_usec_t *sleep_usec, pa_bool_t polled frames = snd_pcm_readi(u->pcm_handle, (uint8_t*) p, (snd_pcm_uframes_t) frames); pa_memblock_release(chunk.memblock); - pa_assert(frames != 0); - if (PA_UNLIKELY(frames < 0)) { pa_memblock_unref(chunk.memblock); - if ((r = try_recover(u, "snd_pcm_readi", (int) (frames))) == 0) + if (!after_avail && (int) frames == -EAGAIN) + break; + + if ((r = try_recover(u, "snd_pcm_readi", (int) frames)) == 0) continue; return r; } + if (!after_avail && frames == 0) { + pa_memblock_unref(chunk.memblock); + break; + } + + pa_assert(frames > 0); + after_avail = FALSE; + chunk.index = 0; chunk.length = (size_t) frames * u->frame_size; @@ -666,15 +767,23 @@ static void update_smoother(struct userdata *u) { now1 = pa_timespec_load(&htstamp); } - position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size); - /* Hmm, if the timestamp is 0, then it wasn't set and we take the current time */ if (now1 <= 0) now1 = pa_rtclock_now(); + /* check if the time since the last update is bigger than the interval */ + if (u->last_smoother_update > 0) + if (u->last_smoother_update + u->smoother_interval > now1) + return; + + position = u->read_count + ((uint64_t) delay * (uint64_t) u->frame_size); now2 = pa_bytes_to_usec(position, &u->source->sample_spec); pa_smoother_put(u->smoother, now1, now2); + + u->last_smoother_update = now1; + /* exponentially increase the update interval up to the MAX limit */ + u->smoother_interval = PA_MIN (u->smoother_interval * 2, SMOOTHER_MAX_INTERVAL); } static pa_usec_t source_get_latency(struct userdata *u) { @@ -780,8 +889,7 @@ static int unsuspend(struct userdata *u) { pa_sample_spec ss; int err; pa_bool_t b, d; - unsigned nfrags; - snd_pcm_uframes_t period_size; + snd_pcm_uframes_t period_size, buffer_size; pa_assert(u); pa_assert(!u->pcm_handle); @@ -789,7 +897,7 @@ static int unsuspend(struct userdata *u) { pa_log_info("Trying resume..."); if ((err = snd_pcm_open(&u->pcm_handle, u->device_name, SND_PCM_STREAM_CAPTURE, - /*SND_PCM_NONBLOCK|*/ + SND_PCM_NONBLOCK| SND_PCM_NO_AUTO_RESAMPLE| SND_PCM_NO_AUTO_CHANNELS| SND_PCM_NO_AUTO_FORMAT)) < 0) { @@ -798,12 +906,12 @@ static int unsuspend(struct userdata *u) { } ss = u->source->sample_spec; - nfrags = u->nfragments; period_size = u->fragment_size / u->frame_size; + buffer_size = u->hwbuf_size / u->frame_size; b = u->use_mmap; d = u->use_tsched; - if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &nfrags, &period_size, u->hwbuf_size / u->frame_size, &b, &d, TRUE)) < 0) { + if ((err = pa_alsa_set_hw_params(u->pcm_handle, &ss, &period_size, &buffer_size, 0, &b, &d, TRUE)) < 0) { pa_log("Failed to set hardware parameters: %s", pa_alsa_strerror(err)); goto fail; } @@ -818,10 +926,11 @@ static int unsuspend(struct userdata *u) { goto fail; } - if (nfrags != u->nfragments || period_size*u->frame_size != u->fragment_size) { - pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu*%lu, New %lu*%lu)", - (unsigned long) u->nfragments, (unsigned long) u->fragment_size, - (unsigned long) nfrags, period_size * u->frame_size); + if (period_size*u->frame_size != u->fragment_size || + buffer_size*u->frame_size != u->hwbuf_size) { + pa_log_warn("Resume failed, couldn't restore original fragment settings. (Old: %lu/%lu, New %lu/%lu)", + (unsigned long) u->hwbuf_size, (unsigned long) u->fragment_size, + (unsigned long) (buffer_size*u->fragment_size), (unsigned long) (period_size*u->frame_size)); goto fail; } @@ -834,7 +943,11 @@ static int unsuspend(struct userdata *u) { /* FIXME: We need to reload the volume somehow */ snd_pcm_start(u->pcm_handle); - pa_smoother_resume(u->smoother, pa_rtclock_now(), TRUE); + + u->read_count = 0; + pa_smoother_reset(u->smoother, pa_rtclock_now(), TRUE); + u->smoother_interval = SMOOTHER_MIN_INTERVAL; + u->last_smoother_update = 0; pa_log_info("Resumed successfully..."); @@ -846,7 +959,7 @@ fail: u->pcm_handle = NULL; } - return -1; + return -PA_ERR_IO; } static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) { @@ -869,30 +982,34 @@ static int source_process_msg(pa_msgobject *o, int code, void *data, int64_t off switch ((pa_source_state_t) PA_PTR_TO_UINT(data)) { - case PA_SOURCE_SUSPENDED: + case PA_SOURCE_SUSPENDED: { + int r; pa_assert(PA_SOURCE_IS_OPENED(u->source->thread_info.state)); - if (suspend(u) < 0) - return -1; + if ((r = suspend(u)) < 0) + return r; break; + } case PA_SOURCE_IDLE: - case PA_SOURCE_RUNNING: + case PA_SOURCE_RUNNING: { + int r; if (u->source->thread_info.state == PA_SOURCE_INIT) { if (build_pollfd(u) < 0) - return -1; + return -PA_ERR_IO; snd_pcm_start(u->pcm_handle); } if (u->source->thread_info.state == PA_SOURCE_SUSPENDED) { - if (unsuspend(u) < 0) - return -1; + if ((r = unsuspend(u)) < 0) + return r; } break; + } case PA_SOURCE_UNLINKED: case PA_SOURCE_INIT: @@ -920,7 +1037,7 @@ static int source_set_state_cb(pa_source *s, pa_source_state_t new_state) { reserve_done(u); else if (old_state == PA_SINK_SUSPENDED && PA_SINK_IS_OPENED(new_state)) if (reserve_init(u, u->device_name) < 0) - return -1; + return -PA_ERR_BUSY; return 0; } @@ -962,15 +1079,11 @@ static void source_get_volume_cb(pa_source *s) { if (pa_cvolume_equal(&u->hardware_volume, &r)) return; - s->virtual_volume = u->hardware_volume = r; - - if (u->mixer_path->has_dB) { - pa_cvolume reset; + s->volume = u->hardware_volume = r; - /* Hmm, so the hardware volume changed, let's reset our software volume */ - pa_cvolume_reset(&reset, s->sample_spec.channels); - pa_source_set_soft_volume(s, &reset); - } + /* Hmm, so the hardware volume changed, let's reset our software volume */ + if (u->mixer_path->has_dB) + pa_source_set_soft_volume(s, NULL); } static void source_set_volume_cb(pa_source *s) { @@ -983,7 +1096,7 @@ static void source_set_volume_cb(pa_source *s) { pa_assert(u->mixer_handle); /* Shift up by the base volume */ - pa_sw_cvolume_divide_scalar(&r, &s->virtual_volume, s->base_volume); + pa_sw_cvolume_divide_scalar(&r, &s->volume, s->base_volume); if (pa_alsa_path_set_volume(u->mixer_path, u->mixer_handle, &s->channel_map, &r) < 0) return; @@ -994,13 +1107,26 @@ static void source_set_volume_cb(pa_source *s) { u->hardware_volume = r; if (u->mixer_path->has_dB) { + pa_cvolume new_soft_volume; + pa_bool_t accurate_enough; /* Match exactly what the user requested by software */ - pa_sw_cvolume_divide(&s->soft_volume, &s->virtual_volume, &u->hardware_volume); + pa_sw_cvolume_divide(&new_soft_volume, &s->volume, &u->hardware_volume); + + /* If the adjustment to do in software is only minimal we + * can skip it. That saves us CPU at the expense of a bit of + * accuracy */ + accurate_enough = + (pa_cvolume_min(&new_soft_volume) >= (PA_VOLUME_NORM - VOLUME_ACCURACY)) && + (pa_cvolume_max(&new_soft_volume) <= (PA_VOLUME_NORM + VOLUME_ACCURACY)); - pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->virtual_volume)); + pa_log_debug("Requested volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->volume)); pa_log_debug("Got hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &u->hardware_volume)); - pa_log_debug("Calculated software volume: %s", pa_cvolume_snprint(t, sizeof(t), &s->soft_volume)); + pa_log_debug("Calculated software volume: %s (accurate-enough=%s)", pa_cvolume_snprint(t, sizeof(t), &new_soft_volume), + pa_yes_no(accurate_enough)); + + if (!accurate_enough) + s->soft_volume = new_soft_volume; } else { pa_log_debug("Wrote hardware volume: %s", pa_cvolume_snprint(t, sizeof(t), &r)); @@ -1008,7 +1134,7 @@ static void source_set_volume_cb(pa_source *s) { /* We can't match exactly what the user requested, hence let's * at least tell the user about it */ - s->virtual_volume = r; + s->volume = r; } } @@ -1107,11 +1233,12 @@ static void thread_func(void *userdata) { if (PA_SOURCE_IS_OPENED(u->source->thread_info.state)) { int work_done; pa_usec_t sleep_usec = 0; + pa_bool_t on_timeout = pa_rtpoll_timer_elapsed(u->rtpoll); if (u->use_mmap) - work_done = mmap_read(u, &sleep_usec, revents & POLLIN); + work_done = mmap_read(u, &sleep_usec, revents & POLLIN, on_timeout); else - work_done = unix_read(u, &sleep_usec, revents & POLLIN); + work_done = unix_read(u, &sleep_usec, revents & POLLIN, on_timeout); if (work_done < 0) goto fail; @@ -1358,8 +1485,8 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p const char *dev_id = NULL; pa_sample_spec ss, requested_ss; pa_channel_map map; - uint32_t nfrags, hwbuf_size, frag_size, tsched_size, tsched_watermark; - snd_pcm_uframes_t period_frames, tsched_frames; + uint32_t nfrags, frag_size, buffer_size, tsched_size, tsched_watermark; + snd_pcm_uframes_t period_frames, buffer_frames, tsched_frames; size_t frame_size; pa_bool_t use_mmap = TRUE, b, use_tsched = TRUE, d, ignore_dB = FALSE; pa_source_new_data data; @@ -1393,8 +1520,10 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p goto fail; } - hwbuf_size = frag_size * nfrags; + buffer_size = nfrags * frag_size; + period_frames = frag_size/frame_size; + buffer_frames = buffer_size/frame_size; tsched_frames = tsched_size/frame_size; if (pa_modargs_get_value_boolean(ma, "mmap", &use_mmap) < 0) { @@ -1433,6 +1562,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p 5, pa_rtclock_now(), FALSE); + u->smoother_interval = SMOOTHER_MIN_INTERVAL; dev_id = pa_modargs_get_value( ma, "device_id", @@ -1459,7 +1589,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, mapping))) goto fail; @@ -1473,7 +1603,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, profile_set, &mapping))) goto fail; @@ -1484,7 +1614,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p &u->device_name, &ss, &map, SND_PCM_STREAM_CAPTURE, - &nfrags, &period_frames, tsched_frames, + &period_frames, &buffer_frames, tsched_frames, &b, &d, FALSE))) goto fail; } @@ -1510,11 +1640,6 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p u->use_tsched = use_tsched = FALSE; } - if (use_tsched && !pa_alsa_pcm_is_hw(u->pcm_handle)) { - pa_log_info("Device is not a hardware device, disabling timer-based scheduling."); - u->use_tsched = use_tsched = FALSE; - } - if (u->use_mmap) pa_log_info("Successfully enabled mmap() mode."); @@ -1536,7 +1661,7 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_alsa_init_proplist_pcm(m->core, data.proplist, u->pcm_handle); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->device_name); - pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (period_frames * frame_size * nfrags)); + pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_BUFFER_SIZE, "%lu", (unsigned long) (buffer_frames * frame_size)); pa_proplist_setf(data.proplist, PA_PROP_DEVICE_BUFFERING_FRAGMENT_SIZE, "%lu", (unsigned long) (period_frames * frame_size)); pa_proplist_sets(data.proplist, PA_PROP_DEVICE_ACCESS_MODE, u->use_tsched ? "mmap+timer" : (u->use_mmap ? "mmap" : "serial")); @@ -1577,18 +1702,25 @@ pa_source *pa_alsa_source_new(pa_module *m, pa_modargs *ma, const char*driver, p pa_source_set_rtpoll(u->source, u->rtpoll); u->frame_size = frame_size; - u->fragment_size = frag_size = (uint32_t) (period_frames * frame_size); - u->nfragments = nfrags; - u->hwbuf_size = u->fragment_size * nfrags; - u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec); + u->fragment_size = frag_size = (size_t) (period_frames * frame_size); + u->hwbuf_size = buffer_size = (size_t) (buffer_frames * frame_size); pa_cvolume_mute(&u->hardware_volume, u->source->sample_spec.channels); - pa_log_info("Using %u fragments of size %lu bytes, buffer time is %0.2fms", - nfrags, (long unsigned) u->fragment_size, + pa_log_info("Using %0.1f fragments of size %lu bytes (%0.2fms), buffer size is %lu bytes (%0.2fms)", + (double) u->hwbuf_size / (double) u->fragment_size, + (long unsigned) u->fragment_size, + (double) pa_bytes_to_usec(u->fragment_size, &ss) / PA_USEC_PER_MSEC, + (long unsigned) u->hwbuf_size, (double) pa_bytes_to_usec(u->hwbuf_size, &ss) / PA_USEC_PER_MSEC); if (u->use_tsched) { - u->watermark_step = pa_usec_to_bytes(TSCHED_WATERMARK_STEP_USEC, &u->source->sample_spec); + u->tsched_watermark = pa_usec_to_bytes_round_up(pa_bytes_to_usec_round_up(tsched_watermark, &requested_ss), &u->source->sample_spec); + + u->watermark_inc_step = pa_usec_to_bytes(TSCHED_WATERMARK_INC_STEP_USEC, &u->source->sample_spec); + u->watermark_dec_step = pa_usec_to_bytes(TSCHED_WATERMARK_DEC_STEP_USEC, &u->source->sample_spec); + + u->watermark_inc_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_INC_THRESHOLD_USEC, &u->source->sample_spec); + u->watermark_dec_threshold = pa_usec_to_bytes_round_up(TSCHED_WATERMARK_DEC_THRESHOLD_USEC, &u->source->sample_spec); fix_min_sleep_wakeup(u); fix_tsched_watermark(u); |