mirror of
https://gitlab.freedesktop.org/pipewire/pipewire.git
synced 2025-10-29 05:40:27 -04:00
alsa: various improvements
Remove some magic constants that are not needed anymore with the headroom. Clamp the error to some reasonable value so that we don't adjust the rate too much but still apply all of the correction calculated by the dll. Remove the bandwidth adjustment. Also update the matching and resample fields when we reassign the node to a new driver. This could cause the new follower to not activate the adaptive resampler and get out of sync.
This commit is contained in:
parent
c8804c3d6d
commit
8981e11c53
2 changed files with 40 additions and 31 deletions
|
|
@ -772,8 +772,8 @@ static int alsa_recover(struct state *state, int err)
|
||||||
delay = SPA_TIMEVAL_TO_USEC(&diff);
|
delay = SPA_TIMEVAL_TO_USEC(&diff);
|
||||||
missing = delay * state->rate / SPA_USEC_PER_SEC;
|
missing = delay * state->rate / SPA_USEC_PER_SEC;
|
||||||
|
|
||||||
spa_log_trace(state->log, NAME" %p: xrun of %"PRIu64" usec %"PRIu64" %f",
|
spa_log_trace(state->log, NAME" %p: xrun of %"PRIu64" usec %"PRIu64,
|
||||||
state, delay, missing, state->safety);
|
state, delay, missing);
|
||||||
|
|
||||||
spa_node_call_xrun(&state->callbacks,
|
spa_node_call_xrun(&state->callbacks,
|
||||||
SPA_TIMEVAL_TO_USEC(&trigger), delay, NULL);
|
SPA_TIMEVAL_TO_USEC(&trigger), delay, NULL);
|
||||||
|
|
@ -828,16 +828,13 @@ static int get_status(struct state *state, snd_pcm_uframes_t *delay, snd_pcm_ufr
|
||||||
|
|
||||||
*target = state->threshold + state->headroom;
|
*target = state->threshold + state->headroom;
|
||||||
|
|
||||||
#define MARGIN 48
|
|
||||||
if (state->resample && state->rate_match) {
|
if (state->resample && state->rate_match) {
|
||||||
state->delay = state->rate_match->delay * 2;
|
state->delay = state->rate_match->delay * 2;
|
||||||
state->read_size = state->rate_match->size;
|
state->read_size = state->rate_match->size;
|
||||||
/* We try to compensate for the latency introduced by rate matching
|
/* We try to compensate for the latency introduced by rate matching
|
||||||
* by moving a little closer to the device read/write pointers.
|
* by moving a little closer to the device read/write pointers. */
|
||||||
* Don't try to get closer than 48 samples but instead increase the
|
if (*target <= state->delay)
|
||||||
* reported latency on the port (TODO). */
|
*target -= SPA_MAX(0, (int)(*target - state->delay));
|
||||||
if (*target <= state->delay + MARGIN)
|
|
||||||
*target -= SPA_MAX(0, (int)(*target - MARGIN - state->delay));
|
|
||||||
else
|
else
|
||||||
*target -= state->delay;
|
*target -= state->delay;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -851,7 +848,6 @@ static int get_status(struct state *state, snd_pcm_uframes_t *delay, snd_pcm_ufr
|
||||||
*delay = avail;
|
*delay = avail;
|
||||||
*target = SPA_MAX(*target, state->read_size);
|
*target = SPA_MAX(*target, state->read_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -863,7 +859,9 @@ static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t del
|
||||||
if (state->stream == SND_PCM_STREAM_PLAYBACK)
|
if (state->stream == SND_PCM_STREAM_PLAYBACK)
|
||||||
err = delay - target;
|
err = delay - target;
|
||||||
else
|
else
|
||||||
err = (target + 128) - delay;
|
err = target - delay;
|
||||||
|
|
||||||
|
err = SPA_CLAMP(err, -state->max_error, state->max_error);
|
||||||
|
|
||||||
if (SPA_UNLIKELY(state->dll.bw == 0.0)) {
|
if (SPA_UNLIKELY(state->dll.bw == 0.0)) {
|
||||||
spa_dll_set_bw(&state->dll, SPA_DLL_BW_MAX, state->threshold, state->rate);
|
spa_dll_set_bw(&state->dll, SPA_DLL_BW_MAX, state->threshold, state->rate);
|
||||||
|
|
@ -874,16 +872,14 @@ static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t del
|
||||||
|
|
||||||
if (SPA_UNLIKELY(state->last_threshold != state->threshold)) {
|
if (SPA_UNLIKELY(state->last_threshold != state->threshold)) {
|
||||||
int32_t diff = (int32_t) (state->last_threshold - state->threshold);
|
int32_t diff = (int32_t) (state->last_threshold - state->threshold);
|
||||||
spa_log_trace(state->log, NAME" %p: follower:%d quantum change %d",
|
spa_log_trace(state->log, NAME" %p: follower:%d quantum change %d -> %d (%d)",
|
||||||
state, follower, diff);
|
state, follower, state->last_threshold, state->threshold, diff);
|
||||||
state->next_time += diff / corr * 1e9 / state->rate;
|
state->next_time += diff / corr * 1e9 / state->rate;
|
||||||
state->last_threshold = state->threshold;
|
state->last_threshold = state->threshold;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SPA_UNLIKELY((state->next_time - state->base_time) > BW_PERIOD)) {
|
if (SPA_UNLIKELY((state->next_time - state->base_time) > BW_PERIOD)) {
|
||||||
state->base_time = state->next_time;
|
state->base_time = state->next_time;
|
||||||
if (state->dll.bw > SPA_DLL_BW_MIN)
|
|
||||||
spa_dll_set_bw(&state->dll, state->dll.bw / 2.0, state->threshold, state->rate);
|
|
||||||
|
|
||||||
spa_log_debug(state->log, NAME" %p: follower:%d match:%d rate:%f "
|
spa_log_debug(state->log, NAME" %p: follower:%d match:%d rate:%f "
|
||||||
"bw:%f thr:%d del:%ld target:%ld err:%f (%f %f %f)",
|
"bw:%f thr:%d del:%ld target:%ld err:%f (%f %f %f)",
|
||||||
|
|
@ -894,9 +890,9 @@ static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t del
|
||||||
|
|
||||||
if (state->rate_match) {
|
if (state->rate_match) {
|
||||||
if (state->stream == SND_PCM_STREAM_PLAYBACK)
|
if (state->stream == SND_PCM_STREAM_PLAYBACK)
|
||||||
state->rate_match->rate = SPA_CLAMP(corr, 0.95, 1.05);
|
state->rate_match->rate = corr;
|
||||||
else
|
else
|
||||||
state->rate_match->rate = SPA_CLAMP(1.0/corr, 0.95, 1.05);
|
state->rate_match->rate = 1.0/corr;
|
||||||
|
|
||||||
SPA_FLAG_UPDATE(state->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE, state->matching);
|
SPA_FLAG_UPDATE(state->rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE, state->matching);
|
||||||
}
|
}
|
||||||
|
|
@ -1206,7 +1202,7 @@ int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence)
|
||||||
spa_log_warn(state->log, NAME" %s: follower resync %ld %d %ld",
|
spa_log_warn(state->log, NAME" %s: follower resync %ld %d %ld",
|
||||||
state->props.device, delay, threshold, target);
|
state->props.device, delay, threshold, target);
|
||||||
if (delay < target)
|
if (delay < target)
|
||||||
snd_pcm_rewind(state->hndl, target - delay + 32);
|
snd_pcm_rewind(state->hndl, target - delay);
|
||||||
else if (delay > target)
|
else if (delay > target)
|
||||||
snd_pcm_forward(state->hndl, delay - target);
|
snd_pcm_forward(state->hndl, delay - target);
|
||||||
|
|
||||||
|
|
@ -1297,14 +1293,14 @@ static int handle_capture(struct state *state, uint64_t nsec,
|
||||||
int res;
|
int res;
|
||||||
struct spa_io_buffers *io;
|
struct spa_io_buffers *io;
|
||||||
|
|
||||||
if (delay < target) {
|
if (SPA_UNLIKELY(delay < target)) {
|
||||||
spa_log_trace(state->log, NAME" %p: early wakeup %ld %ld", state, delay, target);
|
spa_log_trace(state->log, NAME" %p: early wakeup %ld %ld", state, delay, target);
|
||||||
state->next_time = nsec + (target - delay) * SPA_NSEC_PER_SEC /
|
state->next_time = nsec + (target - delay) * SPA_NSEC_PER_SEC /
|
||||||
state->rate;
|
state->rate;
|
||||||
return 0;
|
return -EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((res = update_time(state, nsec, delay, target, false)) < 0)
|
if (SPA_UNLIKELY(res = update_time(state, nsec, delay, target, false)) < 0)
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
if ((res = spa_alsa_read(state, target)) < 0)
|
if ((res = spa_alsa_read(state, target)) < 0)
|
||||||
|
|
@ -1410,6 +1406,24 @@ static inline bool is_following(struct state *state)
|
||||||
return state->position && state->clock && state->position->clock.id != state->clock->id;
|
return state->position && state->clock && state->position->clock.id != state->clock->id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int setup_matching(struct state *state)
|
||||||
|
{
|
||||||
|
int card;
|
||||||
|
|
||||||
|
state->matching = state->following;
|
||||||
|
|
||||||
|
if (state->position == NULL)
|
||||||
|
return -ENOTSUP;
|
||||||
|
|
||||||
|
spa_log_debug(state->log, "clock:%s card:%d", state->position->clock.name, state->card);
|
||||||
|
if (sscanf(state->position->clock.name, "api.alsa.%d", &card) == 1 &&
|
||||||
|
card == state->card) {
|
||||||
|
state->matching = false;
|
||||||
|
}
|
||||||
|
state->resample = (state->rate != state->rate_denom) || state->matching;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int spa_alsa_start(struct state *state)
|
int spa_alsa_start(struct state *state)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
@ -1417,15 +1431,7 @@ int spa_alsa_start(struct state *state)
|
||||||
if (state->started)
|
if (state->started)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
state->following = is_following(state);
|
|
||||||
state->matching = state->following;
|
|
||||||
|
|
||||||
if (state->position) {
|
if (state->position) {
|
||||||
int card;
|
|
||||||
if (sscanf(state->position->clock.name, "api.alsa.%d", &card) == 1 &&
|
|
||||||
card == state->card) {
|
|
||||||
state->matching = false;
|
|
||||||
}
|
|
||||||
state->duration = state->position->clock.duration;
|
state->duration = state->position->clock.duration;
|
||||||
state->rate_denom = state->position->clock.rate.denom;
|
state->rate_denom = state->position->clock.rate.denom;
|
||||||
}
|
}
|
||||||
|
|
@ -1436,12 +1442,14 @@ int spa_alsa_start(struct state *state)
|
||||||
state->rate_denom = state->rate;
|
state->rate_denom = state->rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
state->resample = (state->rate != state->rate_denom) || state->matching;
|
state->following = is_following(state);
|
||||||
|
setup_matching(state);
|
||||||
|
|
||||||
state->threshold = (state->duration * state->rate + state->rate_denom-1) / state->rate_denom;
|
state->threshold = (state->duration * state->rate + state->rate_denom-1) / state->rate_denom;
|
||||||
state->last_threshold = state->threshold;
|
state->last_threshold = state->threshold;
|
||||||
|
|
||||||
spa_dll_init(&state->dll);
|
spa_dll_init(&state->dll);
|
||||||
state->safety = 0.0;
|
state->max_error = (256.0 * state->rate) / state->rate_denom;
|
||||||
|
|
||||||
spa_log_debug(state->log, NAME" %p: start %d duration:%d rate:%d follower:%d match:%d resample:%d",
|
spa_log_debug(state->log, NAME" %p: start %d duration:%d rate:%d follower:%d match:%d resample:%d",
|
||||||
state, state->threshold, state->duration, state->rate_denom,
|
state, state->threshold, state->duration, state->rate_denom,
|
||||||
|
|
@ -1508,6 +1516,7 @@ int spa_alsa_reassign_follower(struct state *state)
|
||||||
state->following = following;
|
state->following = following;
|
||||||
spa_loop_invoke(state->data_loop, do_reassign_follower, 0, NULL, 0, true, state);
|
spa_loop_invoke(state->data_loop, do_reassign_follower, 0, NULL, 0, true, state);
|
||||||
}
|
}
|
||||||
|
setup_matching(state);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -173,9 +173,9 @@ struct state {
|
||||||
uint64_t base_time;
|
uint64_t base_time;
|
||||||
|
|
||||||
uint64_t underrun;
|
uint64_t underrun;
|
||||||
double safety;
|
|
||||||
|
|
||||||
struct spa_dll dll;
|
struct spa_dll dll;
|
||||||
|
double max_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
int
|
int
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue