mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	alsa-seq: use dll to track queue timer
Also increase timer resolution to get less jitter.
This commit is contained in:
		
							parent
							
								
									197d2b15b6
								
							
						
					
					
						commit
						387b281f5c
					
				
					 2 changed files with 89 additions and 11 deletions
				
			
		| 
						 | 
				
			
			@ -240,6 +240,7 @@ int spa_alsa_seq_open(struct seq_state *state)
 | 
			
		|||
	int res;
 | 
			
		||||
	snd_seq_port_subscribe_t *sub;
 | 
			
		||||
	snd_seq_addr_t addr;
 | 
			
		||||
	snd_seq_queue_timer_t *timer;
 | 
			
		||||
 | 
			
		||||
	if (state->opened)
 | 
			
		||||
		return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -281,6 +282,16 @@ int spa_alsa_seq_open(struct seq_state *state)
 | 
			
		|||
	state->sys.source.data = state;
 | 
			
		||||
	spa_loop_add_source(state->main_loop, &state->sys.source);
 | 
			
		||||
 | 
			
		||||
	/* increase queue timer resolution */
 | 
			
		||||
	snd_seq_queue_timer_alloca(&timer);
 | 
			
		||||
	if ((res = snd_seq_get_queue_timer(state->event.hndl, state->event.queue_id, timer)) < 0) {
 | 
			
		||||
		spa_log_warn(state->log, "failed to get queue timer: %s", snd_strerror(res));
 | 
			
		||||
	}
 | 
			
		||||
	snd_seq_queue_timer_set_resolution(timer, INT_MAX);
 | 
			
		||||
	if ((res = snd_seq_set_queue_timer(state->event.hndl, state->event.queue_id, timer)) < 0) {
 | 
			
		||||
		spa_log_warn(state->log, "failed to set queue timer: %s", snd_strerror(res));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	init_ports(state);
 | 
			
		||||
 | 
			
		||||
	if ((res = spa_system_timerfd_create(state->data_system,
 | 
			
		||||
| 
						 | 
				
			
			@ -585,20 +596,78 @@ static int process_write(struct seq_state *state)
 | 
			
		|||
	return SPA_STATUS_NEED_DATA;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int update_time(struct seq_state *state)
 | 
			
		||||
static void init_loop(struct seq_state *state)
 | 
			
		||||
{
 | 
			
		||||
	state->bw = 0.0;
 | 
			
		||||
	state->z1 = state->z2 = state->z3 = 0.0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void set_loop(struct seq_state *state, double bw)
 | 
			
		||||
{
 | 
			
		||||
	double w = 2 * M_PI * bw * state->threshold * state->rate.num / state->rate.denom;
 | 
			
		||||
	state->w0 = 1.0 - exp (-20.0 * w);
 | 
			
		||||
	state->w1 = w * 1.5 / state->threshold;
 | 
			
		||||
	state->w2 = w / 1.5;
 | 
			
		||||
	state->bw = bw;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int update_time(struct seq_state *state, uint64_t nsec, bool slave)
 | 
			
		||||
{
 | 
			
		||||
	snd_seq_queue_status_t *status;
 | 
			
		||||
	const snd_seq_real_time_t* queue_time;
 | 
			
		||||
	uint64_t queue_real, queue_nsec;
 | 
			
		||||
	double err, corr;
 | 
			
		||||
 | 
			
		||||
	/* take queue time */
 | 
			
		||||
	snd_seq_queue_status_alloca(&status);
 | 
			
		||||
        snd_seq_get_queue_status(state->event.hndl, state->event.queue_id, status);
 | 
			
		||||
	queue_time = snd_seq_queue_status_get_real_time(status);
 | 
			
		||||
	state->queue_time = SPA_TIMESPEC_TO_NSEC(queue_time);
 | 
			
		||||
	queue_real = SPA_TIMESPEC_TO_NSEC(queue_time);
 | 
			
		||||
 | 
			
		||||
	spa_log_trace_fp(state->log, "now:%"PRIu64" queue:%"PRIu64" diff:%"PRIu64,
 | 
			
		||||
			state->current_time, state->queue_time,
 | 
			
		||||
			state->current_time - state->queue_time);
 | 
			
		||||
	if (state->queue_start == 0)
 | 
			
		||||
		state->queue_start = state->current_time - queue_real;
 | 
			
		||||
 | 
			
		||||
	state->queue_time = state->current_time - state->queue_start;
 | 
			
		||||
	queue_nsec = state->queue_start + queue_real;
 | 
			
		||||
 | 
			
		||||
	err = ((int64_t)state->current_time - (int64_t) queue_nsec) / state->rate.denom;
 | 
			
		||||
 | 
			
		||||
	if (state->bw == 0.0) {
 | 
			
		||||
		set_loop(state, BW_MAX);
 | 
			
		||||
		state->next_time = nsec;
 | 
			
		||||
		state->base_time = nsec;
 | 
			
		||||
	}
 | 
			
		||||
	state->z1 += state->w0 * (state->w1 * err - state->z1);
 | 
			
		||||
	state->z2 += state->w0 * (state->z1 - state->z2);
 | 
			
		||||
	state->z3 += state->w2 * state->z2;
 | 
			
		||||
 | 
			
		||||
	corr = 1.0 - (state->z2 + state->z3);
 | 
			
		||||
 | 
			
		||||
	if ((state->next_time - state->base_time) > BW_PERIOD) {
 | 
			
		||||
		state->base_time = state->next_time;
 | 
			
		||||
		if (state->bw == BW_MAX)
 | 
			
		||||
			set_loop(state, BW_MED);
 | 
			
		||||
		else if (state->bw == BW_MED)
 | 
			
		||||
			set_loop(state, BW_MIN);
 | 
			
		||||
 | 
			
		||||
		spa_log_debug(state->log, NAME" %p: slave:%d rate:%f bw:%f err:%f (%f %f %f)",
 | 
			
		||||
				state, slave, corr, state->bw, err, state->z1, state->z2, state->z3);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	state->next_time += state->threshold / corr * 1e9 / state->rate.denom;
 | 
			
		||||
 | 
			
		||||
	if (!slave && state->clock) {
 | 
			
		||||
		state->clock->nsec = state->current_time;
 | 
			
		||||
		state->clock->position += state->duration;
 | 
			
		||||
		state->clock->duration = state->duration;
 | 
			
		||||
		state->clock->count = state->clock->position;
 | 
			
		||||
		state->clock->delay = state->duration * corr;
 | 
			
		||||
		state->clock->rate_diff = corr;
 | 
			
		||||
		state->clock->next_nsec = state->next_time;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spa_log_trace_fp(state->log, "now:%"PRIu64" queue:%"PRIu64" next:%"PRIu64" thr:%d",
 | 
			
		||||
			state->current_time, queue_nsec, state->next_time, state->threshold);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -610,7 +679,7 @@ int spa_alsa_seq_process(struct seq_state *state)
 | 
			
		|||
	res = process_recycle(state);
 | 
			
		||||
 | 
			
		||||
	if (state->slaved) {
 | 
			
		||||
		update_time(state);
 | 
			
		||||
		update_time(state, state->position->clock.nsec, true);
 | 
			
		||||
		res |= process_read(state);
 | 
			
		||||
	}
 | 
			
		||||
	res = process_write(state);
 | 
			
		||||
| 
						 | 
				
			
			@ -634,18 +703,15 @@ static void alsa_on_timeout_event(struct spa_source *source)
 | 
			
		|||
		struct spa_io_clock *clock = &state->position->clock;
 | 
			
		||||
		state->rate = clock->rate;
 | 
			
		||||
		state->duration = clock->duration;
 | 
			
		||||
		state->threshold = (state->duration * clock->rate.num * SPA_NSEC_PER_SEC) /
 | 
			
		||||
			clock->rate.denom;
 | 
			
		||||
		state->threshold = state->duration;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	update_time(state);
 | 
			
		||||
	update_time(state, state->current_time, false);
 | 
			
		||||
 | 
			
		||||
	res = process_read(state);
 | 
			
		||||
	if (res > 0)
 | 
			
		||||
		spa_node_call_ready(&state->callbacks, res);
 | 
			
		||||
 | 
			
		||||
	state->next_time += state->threshold;
 | 
			
		||||
 | 
			
		||||
	set_timeout(state, state->next_time);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -708,6 +774,7 @@ int spa_alsa_seq_start(struct seq_state *state)
 | 
			
		|||
	state->source.rmask = 0;
 | 
			
		||||
	spa_loop_add_source(state->data_loop, &state->source);
 | 
			
		||||
 | 
			
		||||
	init_loop(state);
 | 
			
		||||
	set_timers(state);
 | 
			
		||||
 | 
			
		||||
	state->started = true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -105,6 +105,11 @@ struct seq_conn {
 | 
			
		|||
	struct spa_source source;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define BW_MAX		0.128
 | 
			
		||||
#define BW_MED		0.064
 | 
			
		||||
#define BW_MIN		0.016
 | 
			
		||||
#define BW_PERIOD	(3 * SPA_NSEC_PER_SEC)
 | 
			
		||||
 | 
			
		||||
struct seq_state {
 | 
			
		||||
	struct spa_handle handle;
 | 
			
		||||
	struct spa_node node;
 | 
			
		||||
| 
						 | 
				
			
			@ -139,13 +144,19 @@ struct seq_state {
 | 
			
		|||
	int timerfd;
 | 
			
		||||
	uint64_t current_time;
 | 
			
		||||
	uint64_t next_time;
 | 
			
		||||
	uint64_t base_time;
 | 
			
		||||
	uint64_t queue_time;
 | 
			
		||||
	uint64_t queue_start;
 | 
			
		||||
 | 
			
		||||
	unsigned int opened:1;
 | 
			
		||||
	unsigned int started:1;
 | 
			
		||||
	unsigned int slaved:1;
 | 
			
		||||
 | 
			
		||||
	struct seq_stream streams[2];
 | 
			
		||||
 | 
			
		||||
	double bw;
 | 
			
		||||
	double z1, z2, z3;
 | 
			
		||||
	double w0, w1, w2;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define VALID_DIRECTION(this,d)		((d) == SPA_DIRECTION_INPUT || (d) == SPA_DIRECTION_OUTPUT)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue