| 
									
										
										
										
											2023-02-08 18:12:00 +01:00
										 |  |  | /* Spa */ | 
					
						
							|  |  |  | /* SPDX-FileCopyrightText: Copyright © 2020 Wim Taymans */ | 
					
						
							|  |  |  | /* SPDX-License-Identifier: MIT */ | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | #include <stdbool.h>
 | 
					
						
							|  |  |  | #include <limits.h>
 | 
					
						
							| 
									
										
										
										
											2022-03-13 15:07:23 +01:00
										 |  |  | #include <getopt.h>
 | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | #include <math.h>
 | 
					
						
							|  |  |  | #include <sys/timerfd.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <alsa/asoundlib.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-30 17:22:26 +02:00
										 |  |  | #include <spa/utils/dll.h>
 | 
					
						
							| 
									
										
										
										
											2022-05-11 10:34:27 +02:00
										 |  |  | #include <spa/utils/defs.h>
 | 
					
						
							| 
									
										
										
										
											2020-12-09 12:09:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | #define DEFAULT_DEVICE	"hw:0"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-18 15:41:12 +02:00
										 |  |  | #define M_PI_M2f (float)(M_PI+M_PI)
 | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-11 10:34:27 +02:00
										 |  |  | #define BW_PERIOD	(SPA_NSEC_PER_SEC * 3)
 | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | struct state { | 
					
						
							| 
									
										
										
										
											2022-03-13 15:07:23 +01:00
										 |  |  | 	const char *device; | 
					
						
							|  |  |  | 	unsigned int format; | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	unsigned int rate; | 
					
						
							|  |  |  | 	unsigned int channels; | 
					
						
							|  |  |  | 	snd_pcm_uframes_t period; | 
					
						
							| 
									
										
										
										
											2022-02-03 16:17:38 +01:00
										 |  |  | 	snd_pcm_uframes_t buffer_frames; | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	snd_pcm_t *hndl; | 
					
						
							|  |  |  | 	int timerfd; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-03 16:17:38 +01:00
										 |  |  | 	double max_error; | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	float accumulator; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	uint64_t next_time; | 
					
						
							|  |  |  | 	uint64_t prev_time; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-09 12:09:40 +01:00
										 |  |  | 	struct spa_dll dll; | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int set_timeout(struct state *state, uint64_t time) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct itimerspec ts; | 
					
						
							| 
									
										
										
										
											2022-05-11 10:34:27 +02:00
										 |  |  | 	ts.it_value.tv_sec = time / SPA_NSEC_PER_SEC; | 
					
						
							|  |  |  | 	ts.it_value.tv_nsec = time % SPA_NSEC_PER_SEC; | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	ts.it_interval.tv_sec = 0; | 
					
						
							|  |  |  | 	ts.it_interval.tv_nsec = 0; | 
					
						
							|  |  |  | 	return timerfd_settime(state->timerfd, TFD_TIMER_ABSTIME, &ts, NULL); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CHECK(s,msg,...) {		\
 | 
					
						
							|  |  |  | 	int __err;			\ | 
					
						
							|  |  |  | 	if ((__err = (s)) < 0) {	\ | 
					
						
							|  |  |  | 		fprintf(stderr, msg ": %s\n", ##__VA_ARGS__, snd_strerror(__err));	\ | 
					
						
							|  |  |  | 		return __err;		\ | 
					
						
							|  |  |  | 	}				\ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-13 15:07:23 +01:00
										 |  |  | #define LOOP(type,areas,scale) {									\
 | 
					
						
							|  |  |  | 	uint32_t i, j;											\ | 
					
						
							|  |  |  | 	type *samples, v;										\ | 
					
						
							|  |  |  | 	samples = (type*)((uint8_t*)areas[0].addr + (areas[0].first + offset*areas[0].step) / 8);	\ | 
					
						
							|  |  |  | 	for (i = 0; i < frames; i++) {									\ | 
					
						
							| 
									
										
										
										
											2024-06-18 12:17:56 +02:00
										 |  |  | 		state->accumulator += M_PI_M2f * 440.0f / state->rate;					\ | 
					
						
							|  |  |  | 		if (state->accumulator >= M_PI_M2f)							\ | 
					
						
							|  |  |  | 			state->accumulator -= M_PI_M2f;							\ | 
					
						
							|  |  |  | 		v = (type)(sin(state->accumulator) * scale);						\ | 
					
						
							| 
									
										
										
										
											2022-03-13 15:07:23 +01:00
										 |  |  | 		for (j = 0; j < state->channels; j++)							\ | 
					
						
							|  |  |  | 			*samples++ = v;									\ | 
					
						
							|  |  |  | 	}												\ | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | static int write_period(struct state *state) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	snd_pcm_uframes_t frames = state->period; | 
					
						
							|  |  |  | 	snd_pcm_uframes_t offset; | 
					
						
							|  |  |  | 	const snd_pcm_channel_area_t* areas; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snd_pcm_mmap_begin(state->hndl, &areas, &offset, &frames); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-13 15:07:23 +01:00
										 |  |  | 	switch (state->format) { | 
					
						
							|  |  |  | 	case SND_PCM_FORMAT_S32_LE: | 
					
						
							|  |  |  | 		LOOP(int32_t, areas, 0x7fffffff); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	case SND_PCM_FORMAT_S16_LE: | 
					
						
							|  |  |  | 		LOOP(int16_t, areas, 0x7fff); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		break; | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-03-13 15:07:23 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	snd_pcm_mmap_commit(state->hndl, offset, frames) ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int on_timer_wakeup(struct state *state) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2022-02-03 16:17:38 +01:00
										 |  |  | 	snd_pcm_sframes_t delay; | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	double error, corr; | 
					
						
							| 
									
										
										
										
											2022-02-03 16:17:38 +01:00
										 |  |  | #if 1
 | 
					
						
							|  |  |  | 	snd_pcm_sframes_t avail; | 
					
						
							|  |  |  |         CHECK(snd_pcm_avail_delay(state->hndl, &avail, &delay), "delay"); | 
					
						
							|  |  |  | #else
 | 
					
						
							|  |  |  | 	snd_pcm_uframes_t avail; | 
					
						
							|  |  |  | 	snd_htimestamp_t tstamp; | 
					
						
							|  |  |  | 	uint64_t then; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	CHECK(snd_pcm_htimestamp(state->hndl, &avail, &tstamp), "htimestamp"); | 
					
						
							|  |  |  | 	delay = state->buffer_frames - avail; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-05-11 10:34:27 +02:00
										 |  |  | 	then = SPA_TIMESPEC_TO_NSEC(&tstamp); | 
					
						
							| 
									
										
										
										
											2022-02-03 16:17:38 +01:00
										 |  |  | 	if (then != 0) { | 
					
						
							|  |  |  | 		if (then < state->next_time) { | 
					
						
							| 
									
										
										
										
											2022-05-11 10:34:27 +02:00
										 |  |  | 			delay -= (state->next_time - then) * state->rate / SPA_NSEC_PER_SEC; | 
					
						
							| 
									
										
										
										
											2022-02-03 16:17:38 +01:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2022-05-11 10:34:27 +02:00
										 |  |  | 			delay += (then - state->next_time) * state->rate / SPA_NSEC_PER_SEC; | 
					
						
							| 
									
										
										
										
											2022-02-03 16:17:38 +01:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* calculate the error, we want to have exactly 1 period of
 | 
					
						
							|  |  |  | 	 * samples remaining in the device when we wakeup. */ | 
					
						
							|  |  |  | 	error = (double)delay - (double)state->period; | 
					
						
							| 
									
										
										
										
											2022-02-03 16:17:38 +01:00
										 |  |  | 	if (error > state->max_error) | 
					
						
							|  |  |  | 		error = state->max_error; | 
					
						
							|  |  |  | 	else if (error < -state->max_error) | 
					
						
							|  |  |  | 		error = -state->max_error; | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-09 12:09:40 +01:00
										 |  |  | 	/* update the dll with the error, this gives a rate correction */ | 
					
						
							|  |  |  | 	corr = spa_dll_update(&state->dll, error); | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* set our new adjusted timeout. alternatively, this value can
 | 
					
						
							|  |  |  | 	 * instead be used to drive a resampler if this device is | 
					
						
							|  |  |  | 	 * slaved. */ | 
					
						
							| 
									
										
										
										
											2024-06-18 12:17:56 +02:00
										 |  |  | 	state->next_time += (uint64_t)(state->period / corr * 1e9 / state->rate); | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	set_timeout(state, state->next_time); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (state->next_time - state->prev_time > BW_PERIOD) { | 
					
						
							|  |  |  | 		state->prev_time = state->next_time; | 
					
						
							|  |  |  | 		fprintf(stdout, "corr:%f error:%f bw:%f\n", | 
					
						
							| 
									
										
										
										
											2020-12-09 12:09:40 +01:00
										 |  |  | 				corr, error, state->dll.bw); | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	/* pull in new samples write a new period */ | 
					
						
							|  |  |  | 	write_period(state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-13 15:07:23 +01:00
										 |  |  | static unsigned int format_from_string(const char *str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	if (strcmp(str, "S32_LE") == 0) | 
					
						
							|  |  |  | 		return SND_PCM_FORMAT_S32_LE; | 
					
						
							|  |  |  | 	else if (strcmp(str, "S32_BE") == 0) | 
					
						
							|  |  |  | 		return SND_PCM_FORMAT_S32_BE; | 
					
						
							|  |  |  | 	else if (strcmp(str, "S24_LE") == 0) | 
					
						
							|  |  |  | 		return SND_PCM_FORMAT_S24_LE; | 
					
						
							|  |  |  | 	else if (strcmp(str, "S24_BE") == 0) | 
					
						
							|  |  |  | 		return SND_PCM_FORMAT_S24_BE; | 
					
						
							|  |  |  | 	else if (strcmp(str, "S24_3LE") == 0) | 
					
						
							|  |  |  | 		return SND_PCM_FORMAT_S24_3LE; | 
					
						
							|  |  |  | 	else if (strcmp(str, "S24_3_BE") == 0) | 
					
						
							|  |  |  | 		return SND_PCM_FORMAT_S24_3BE; | 
					
						
							|  |  |  | 	else if (strcmp(str, "S16_LE") == 0) | 
					
						
							|  |  |  | 		return SND_PCM_FORMAT_S16_LE; | 
					
						
							|  |  |  | 	else if (strcmp(str, "S16_BE") == 0) | 
					
						
							|  |  |  | 		return SND_PCM_FORMAT_S16_BE; | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void show_help(const char *name, bool error) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |         fprintf(error ? stderr : stdout, "%s [options]\n" | 
					
						
							|  |  |  | 		"  -h, --help                            Show this help\n" | 
					
						
							|  |  |  | 		"  -D, --device                          device name (default %s)\n", | 
					
						
							|  |  |  | 		name, DEFAULT_DEVICE); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | int main(int argc, char *argv[]) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct state state = { 0, }; | 
					
						
							|  |  |  | 	snd_pcm_hw_params_t *hparams; | 
					
						
							| 
									
										
										
										
											2022-02-03 16:17:38 +01:00
										 |  |  | 	snd_pcm_sw_params_t *sparams; | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	struct timespec now; | 
					
						
							| 
									
										
										
										
											2022-04-19 16:48:27 +02:00
										 |  |  | 	int c; | 
					
						
							| 
									
										
										
										
											2022-03-13 15:07:23 +01:00
										 |  |  | 	static const struct option long_options[] = { | 
					
						
							|  |  |  | 		{ "help",	no_argument,		NULL, 'h' }, | 
					
						
							|  |  |  | 		{ "device",	required_argument,	NULL, 'D' }, | 
					
						
							|  |  |  | 		{ "format",	required_argument,	NULL, 'f' }, | 
					
						
							|  |  |  | 		{ "rate",	required_argument,	NULL, 'r' }, | 
					
						
							|  |  |  | 		{ "channels",	required_argument,	NULL, 'c' }, | 
					
						
							|  |  |  | 		{ NULL, 0, NULL, 0} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	state.device = DEFAULT_DEVICE; | 
					
						
							|  |  |  | 	state.format = SND_PCM_FORMAT_S16_LE; | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	state.rate = 44100; | 
					
						
							|  |  |  | 	state.channels = 2; | 
					
						
							|  |  |  | 	state.period = 1024; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-13 15:07:23 +01:00
										 |  |  | 	while ((c = getopt_long(argc, argv, "hD:f:r:c:", long_options, NULL)) != -1) { | 
					
						
							|  |  |  | 		switch (c) { | 
					
						
							|  |  |  | 		case 'h': | 
					
						
							|  |  |  | 			show_help(argv[0], false); | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		case 'D': | 
					
						
							|  |  |  | 			state.device = optarg; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 'f': | 
					
						
							|  |  |  | 			state.format = format_from_string(optarg); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 'r': | 
					
						
							|  |  |  | 			state.rate = atoi(optarg); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 'c': | 
					
						
							|  |  |  | 			state.channels = atoi(optarg); | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			show_help(argv[0], true); | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	CHECK(snd_pcm_open(&state.hndl, state.device, SND_PCM_STREAM_PLAYBACK, 0), | 
					
						
							|  |  |  | 			"open %s failed", state.device); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	/* hw params */ | 
					
						
							|  |  |  | 	snd_pcm_hw_params_alloca(&hparams); | 
					
						
							|  |  |  | 	snd_pcm_hw_params_any(state.hndl, hparams); | 
					
						
							|  |  |  | 	CHECK(snd_pcm_hw_params_set_access(state.hndl, hparams, | 
					
						
							|  |  |  | 				SND_PCM_ACCESS_MMAP_INTERLEAVED), "set interleaved"); | 
					
						
							|  |  |  | 	CHECK(snd_pcm_hw_params_set_format(state.hndl, hparams, | 
					
						
							| 
									
										
										
										
											2022-03-13 15:07:23 +01:00
										 |  |  | 				state.format), "set format"); | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	CHECK(snd_pcm_hw_params_set_channels_near(state.hndl, hparams, | 
					
						
							|  |  |  | 				&state.channels), "set channels"); | 
					
						
							|  |  |  | 	CHECK(snd_pcm_hw_params_set_rate_near(state.hndl, hparams, | 
					
						
							|  |  |  | 				&state.rate, 0), "set rate"); | 
					
						
							|  |  |  | 	CHECK(snd_pcm_hw_params(state.hndl, hparams), "hw_params"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-03 16:17:38 +01:00
										 |  |  | 	CHECK(snd_pcm_hw_params_get_buffer_size(hparams, &state.buffer_frames), "get_buffer_size_max"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	fprintf(stdout, "opened format:%s rate:%u channels:%u\n", | 
					
						
							| 
									
										
										
										
											2022-03-13 15:07:23 +01:00
										 |  |  | 			snd_pcm_format_name(state.format), | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 			state.rate, state.channels); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-03 16:17:38 +01:00
										 |  |  | 	snd_pcm_sw_params_alloca(&sparams); | 
					
						
							|  |  |  | #if 0
 | 
					
						
							|  |  |  | 	CHECK(snd_pcm_sw_params_current(state.hndl, sparams), "sw_params_current"); | 
					
						
							|  |  |  | 	CHECK(snd_pcm_sw_params_set_tstamp_mode(state.hndl, sparams, SND_PCM_TSTAMP_ENABLE), | 
					
						
							|  |  |  | 			"sw_params_set_tstamp_type"); | 
					
						
							|  |  |  | 	CHECK(snd_pcm_sw_params_set_tstamp_type(state.hndl, sparams, SND_PCM_TSTAMP_TYPE_MONOTONIC), | 
					
						
							|  |  |  | 			"sw_params_set_tstamp_type"); | 
					
						
							|  |  |  | 	CHECK(snd_pcm_sw_params(state.hndl, sparams), "sw_params"); | 
					
						
							|  |  |  | #endif
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-09 12:09:40 +01:00
										 |  |  | 	spa_dll_init(&state.dll); | 
					
						
							|  |  |  | 	spa_dll_set_bw(&state.dll, SPA_DLL_BW_MAX, state.period, state.rate); | 
					
						
							| 
									
										
										
										
											2022-05-11 10:34:27 +02:00
										 |  |  | 	state.max_error = SPA_MAX(256.0, state.period / 2.0f); | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if ((state.timerfd = timerfd_create(CLOCK_MONOTONIC, 0)) < 0) | 
					
						
							|  |  |  | 		perror("timerfd"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	CHECK(snd_pcm_prepare(state.hndl), "prepare"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* before we start, write one period */ | 
					
						
							|  |  |  | 	write_period(&state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set our first timeout for now */ | 
					
						
							|  |  |  | 	clock_gettime(CLOCK_MONOTONIC, &now); | 
					
						
							| 
									
										
										
										
											2022-05-11 10:34:27 +02:00
										 |  |  | 	state.prev_time = state.next_time = SPA_TIMESPEC_TO_NSEC(&now); | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 	set_timeout(&state, state.next_time); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* and start playback */ | 
					
						
							|  |  |  | 	CHECK(snd_pcm_start(state.hndl), "start"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* wait for timer to expire and call the wakeup function,
 | 
					
						
							|  |  |  | 	 * this can be done in a poll loop as well */ | 
					
						
							|  |  |  | 	while (true) { | 
					
						
							|  |  |  | 		uint64_t expirations; | 
					
						
							| 
									
										
										
										
											2020-12-24 17:52:36 +02:00
										 |  |  | 		CHECK(read(state.timerfd, &expirations, sizeof(expirations)), "read"); | 
					
						
							| 
									
										
										
										
											2020-11-27 10:30:54 +01:00
										 |  |  | 		on_timer_wakeup(&state); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	snd_pcm_drain(state.hndl); | 
					
						
							|  |  |  | 	snd_pcm_close(state.hndl); | 
					
						
							|  |  |  | 	close(state.timerfd); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return EXIT_SUCCESS; | 
					
						
							|  |  |  | } |