output_plugin: the plugin allocates the audio_output object
Pass audio_output objects around instead of void pointers. This will give some more control to the plugin, and prepares for non-blocking audio outputs.
This commit is contained in:
		| @@ -45,7 +45,6 @@ mpd_headers = \ | |||||||
| 	src/audio_parser.h \ | 	src/audio_parser.h \ | ||||||
| 	src/output_internal.h \ | 	src/output_internal.h \ | ||||||
| 	src/output_api.h \ | 	src/output_api.h \ | ||||||
| 	src/output_plugin.h \ |  | ||||||
| 	src/output_list.h \ | 	src/output_list.h \ | ||||||
| 	src/output_all.h \ | 	src/output_all.h \ | ||||||
| 	src/output_thread.h \ | 	src/output_thread.h \ | ||||||
| @@ -716,6 +715,7 @@ OUTPUT_API_SRC = \ | |||||||
| 	src/output_state.c \ | 	src/output_state.c \ | ||||||
| 	src/output_print.c \ | 	src/output_print.c \ | ||||||
| 	src/output_command.c \ | 	src/output_command.c \ | ||||||
|  | 	src/output_plugin.c src/output_plugin.h \ | ||||||
| 	src/output_finish.c \ | 	src/output_finish.c \ | ||||||
| 	src/output_init.c | 	src/output_init.c | ||||||
|  |  | ||||||
| @@ -1144,6 +1144,7 @@ test_run_output_SOURCES = test/run_output.c \ | |||||||
| 	src/page.c \ | 	src/page.c \ | ||||||
| 	src/socket_util.c \ | 	src/socket_util.c \ | ||||||
| 	src/output_init.c src/output_finish.c src/output_list.c \ | 	src/output_init.c src/output_finish.c src/output_list.c \ | ||||||
|  | 	src/output_plugin.c \ | ||||||
| 	$(ENCODER_SRC) \ | 	$(ENCODER_SRC) \ | ||||||
| 	src/mixer_api.c \ | 	src/mixer_api.c \ | ||||||
| 	src/mixer_control.c \ | 	src/mixer_control.c \ | ||||||
|   | |||||||
| @@ -43,6 +43,8 @@ typedef snd_pcm_sframes_t alsa_writei_t(snd_pcm_t * pcm, const void *buffer, | |||||||
| 					snd_pcm_uframes_t size); | 					snd_pcm_uframes_t size); | ||||||
|  |  | ||||||
| struct alsa_data { | struct alsa_data { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	/** the configured name of the ALSA device; NULL for the | 	/** the configured name of the ALSA device; NULL for the | ||||||
| 	    default device */ | 	    default device */ | ||||||
| 	char *device; | 	char *device; | ||||||
| @@ -143,23 +145,27 @@ alsa_configure(struct alsa_data *ad, const struct config_param *param) | |||||||
| #endif | #endif | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| alsa_init(G_GNUC_UNUSED const struct audio_format *audio_format, | alsa_init(const struct config_param *param, GError **error_r) | ||||||
| 	  const struct config_param *param, |  | ||||||
| 	  G_GNUC_UNUSED GError **error) |  | ||||||
| { | { | ||||||
| 	struct alsa_data *ad = alsa_data_new(); | 	struct alsa_data *ad = alsa_data_new(); | ||||||
|  |  | ||||||
|  | 	if (!ao_base_init(&ad->base, &alsa_output_plugin, param, error_r)) { | ||||||
|  | 		g_free(ad); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	alsa_configure(ad, param); | 	alsa_configure(ad, param); | ||||||
|  |  | ||||||
| 	return ad; | 	return &ad->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| alsa_finish(void *data) | alsa_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct alsa_data *ad = data; | 	struct alsa_data *ad = (struct alsa_data *)ao; | ||||||
|  |  | ||||||
|  | 	ao_base_finish(&ad->base); | ||||||
| 	alsa_data_free(ad); | 	alsa_data_free(ad); | ||||||
|  |  | ||||||
| 	/* free libasound's config cache */ | 	/* free libasound's config cache */ | ||||||
| @@ -530,9 +536,9 @@ error: | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| alsa_open(void *data, struct audio_format *audio_format, GError **error) | alsa_open(struct audio_output *ao, struct audio_format *audio_format, GError **error) | ||||||
| { | { | ||||||
| 	struct alsa_data *ad = data; | 	struct alsa_data *ad = (struct alsa_data *)ao; | ||||||
| 	int err; | 	int err; | ||||||
| 	bool success; | 	bool success; | ||||||
|  |  | ||||||
| @@ -594,9 +600,9 @@ alsa_recover(struct alsa_data *ad, int err) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| alsa_drain(void *data) | alsa_drain(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct alsa_data *ad = data; | 	struct alsa_data *ad = (struct alsa_data *)ao; | ||||||
|  |  | ||||||
| 	if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING) | 	if (snd_pcm_state(ad->pcm) != SND_PCM_STATE_RUNNING) | ||||||
| 		return; | 		return; | ||||||
| @@ -628,9 +634,9 @@ alsa_drain(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| alsa_cancel(void *data) | alsa_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct alsa_data *ad = data; | 	struct alsa_data *ad = (struct alsa_data *)ao; | ||||||
|  |  | ||||||
| 	ad->period_position = 0; | 	ad->period_position = 0; | ||||||
|  |  | ||||||
| @@ -638,17 +644,18 @@ alsa_cancel(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| alsa_close(void *data) | alsa_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct alsa_data *ad = data; | 	struct alsa_data *ad = (struct alsa_data *)ao; | ||||||
|  |  | ||||||
| 	snd_pcm_close(ad->pcm); | 	snd_pcm_close(ad->pcm); | ||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| alsa_play(void *data, const void *chunk, size_t size, GError **error) | alsa_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
|  | 	  GError **error) | ||||||
| { | { | ||||||
| 	struct alsa_data *ad = data; | 	struct alsa_data *ad = (struct alsa_data *)ao; | ||||||
|  |  | ||||||
| 	size /= ad->frame_size; | 	size /= ad->frame_size; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -33,6 +33,8 @@ static const ao_sample_format OUR_AO_FORMAT_INITIALIZER; | |||||||
| static unsigned ao_output_ref; | static unsigned ao_output_ref; | ||||||
|  |  | ||||||
| struct ao_data { | struct ao_data { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	size_t write_size; | 	size_t write_size; | ||||||
| 	int driver; | 	int driver; | ||||||
| 	ao_option *options; | 	ao_option *options; | ||||||
| @@ -79,12 +81,17 @@ ao_output_error(GError **error_r) | |||||||
| 		    "%s", error); | 		    "%s", error); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | ao_output_init(const struct config_param *param, | ||||||
| 	       const struct config_param *param, |  | ||||||
| 	       GError **error) | 	       GError **error) | ||||||
| { | { | ||||||
| 	struct ao_data *ad = g_new(struct ao_data, 1); | 	struct ao_data *ad = g_new(struct ao_data, 1); | ||||||
|  |  | ||||||
|  | 	if (!ao_base_init(&ad->base, &ao_output_plugin, param, error)) { | ||||||
|  | 		g_free(ad); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	ao_info *ai; | 	ao_info *ai; | ||||||
| 	const char *value; | 	const char *value; | ||||||
|  |  | ||||||
| @@ -107,6 +114,7 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 		g_set_error(error, ao_output_quark(), 0, | 		g_set_error(error, ao_output_quark(), 0, | ||||||
| 			    "\"%s\" is not a valid ao driver", | 			    "\"%s\" is not a valid ao driver", | ||||||
| 			    value); | 			    value); | ||||||
|  | 		ao_base_finish(&ad->base); | ||||||
| 		g_free(ad); | 		g_free(ad); | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
| @@ -114,6 +122,7 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 	if ((ai = ao_driver_info(ad->driver)) == NULL) { | 	if ((ai = ao_driver_info(ad->driver)) == NULL) { | ||||||
| 		g_set_error(error, ao_output_quark(), 0, | 		g_set_error(error, ao_output_quark(), 0, | ||||||
| 			    "problems getting driver info"); | 			    "problems getting driver info"); | ||||||
|  | 		ao_base_finish(&ad->base); | ||||||
| 		g_free(ad); | 		g_free(ad); | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
| @@ -132,6 +141,7 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 				g_set_error(error, ao_output_quark(), 0, | 				g_set_error(error, ao_output_quark(), 0, | ||||||
| 					    "problems parsing options \"%s\"", | 					    "problems parsing options \"%s\"", | ||||||
| 					    options[i]); | 					    options[i]); | ||||||
|  | 				ao_base_finish(&ad->base); | ||||||
| 				g_free(ad); | 				g_free(ad); | ||||||
| 				return NULL; | 				return NULL; | ||||||
| 			} | 			} | ||||||
| @@ -145,15 +155,16 @@ ao_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 		g_strfreev(options); | 		g_strfreev(options); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return ad; | 	return &ad->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| ao_output_finish(void *data) | ao_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct ao_data *ad = (struct ao_data *)data; | 	struct ao_data *ad = (struct ao_data *)ao; | ||||||
|  |  | ||||||
| 	ao_free_options(ad->options); | 	ao_free_options(ad->options); | ||||||
|  | 	ao_base_finish(&ad->base); | ||||||
| 	g_free(ad); | 	g_free(ad); | ||||||
|  |  | ||||||
| 	ao_output_ref--; | 	ao_output_ref--; | ||||||
| @@ -163,19 +174,19 @@ ao_output_finish(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| ao_output_close(void *data) | ao_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct ao_data *ad = (struct ao_data *)data; | 	struct ao_data *ad = (struct ao_data *)ao; | ||||||
|  |  | ||||||
| 	ao_close(ad->device); | 	ao_close(ad->device); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| ao_output_open(void *data, struct audio_format *audio_format, | ao_output_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
| 	       GError **error) | 	       GError **error) | ||||||
| { | { | ||||||
| 	ao_sample_format format = OUR_AO_FORMAT_INITIALIZER; | 	ao_sample_format format = OUR_AO_FORMAT_INITIALIZER; | ||||||
| 	struct ao_data *ad = (struct ao_data *)data; | 	struct ao_data *ad = (struct ao_data *)ao; | ||||||
|  |  | ||||||
| 	switch (audio_format->format) { | 	switch (audio_format->format) { | ||||||
| 	case SAMPLE_FORMAT_S8: | 	case SAMPLE_FORMAT_S8: | ||||||
| @@ -227,10 +238,10 @@ static int ao_play_deconst(ao_device *device, const void *output_samples, | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| ao_output_play(void *data, const void *chunk, size_t size, | ao_output_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
| 	       GError **error) | 	       GError **error) | ||||||
| { | { | ||||||
| 	struct ao_data *ad = (struct ao_data *)data; | 	struct ao_data *ad = (struct ao_data *)ao; | ||||||
|  |  | ||||||
| 	if (size > ad->write_size) | 	if (size > ad->write_size) | ||||||
| 		size = ad->write_size; | 		size = ad->write_size; | ||||||
|   | |||||||
| @@ -54,6 +54,8 @@ struct mpd_ffado_stream { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| struct mpd_ffado_device { | struct mpd_ffado_device { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	char *device_name; | 	char *device_name; | ||||||
| 	int verbose; | 	int verbose; | ||||||
| 	unsigned period_size, nb_buffers; | 	unsigned period_size, nb_buffers; | ||||||
| @@ -83,21 +85,26 @@ ffado_output_quark(void) | |||||||
| 	return g_quark_from_static_string("ffado_output"); | 	return g_quark_from_static_string("ffado_output"); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| ffado_init(G_GNUC_UNUSED const struct audio_format *audio_format, | ffado_init(const struct config_param *param, | ||||||
| 	   const struct config_param *param, |  | ||||||
| 	   GError **error_r) | 	   GError **error_r) | ||||||
| { | { | ||||||
| 	g_debug("using libffado version %s, API=%d", | 	g_debug("using libffado version %s, API=%d", | ||||||
| 		ffado_get_version(), ffado_get_api_version()); | 		ffado_get_version(), ffado_get_api_version()); | ||||||
|  |  | ||||||
| 	struct mpd_ffado_device *fd = g_new(struct mpd_ffado_device, 1); | 	struct mpd_ffado_device *fd = g_new(struct mpd_ffado_device, 1); | ||||||
|  | 	if (!ao_base_init(&fd->base, &ffado_output_plugin, param, error_r)) { | ||||||
|  | 		g_free(fd); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	fd->device_name = config_dup_block_string(param, "device", NULL); | 	fd->device_name = config_dup_block_string(param, "device", NULL); | ||||||
| 	fd->verbose = config_get_block_unsigned(param, "verbose", 0); | 	fd->verbose = config_get_block_unsigned(param, "verbose", 0); | ||||||
|  |  | ||||||
| 	fd->period_size = config_get_block_unsigned(param, "period_size", | 	fd->period_size = config_get_block_unsigned(param, "period_size", | ||||||
| 						    1024); | 						    1024); | ||||||
| 	if (fd->period_size == 0 || fd->period_size > 1024 * 1024) { | 	if (fd->period_size == 0 || fd->period_size > 1024 * 1024) { | ||||||
|  | 		ao_base_finish(&fd->base); | ||||||
| 		g_set_error(error_r, ffado_output_quark(), 0, | 		g_set_error(error_r, ffado_output_quark(), 0, | ||||||
| 			    "invalid period_size setting"); | 			    "invalid period_size setting"); | ||||||
| 		return false; | 		return false; | ||||||
| @@ -105,20 +112,22 @@ ffado_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
|  |  | ||||||
| 	fd->nb_buffers = config_get_block_unsigned(param, "nb_buffers", 3); | 	fd->nb_buffers = config_get_block_unsigned(param, "nb_buffers", 3); | ||||||
| 	if (fd->nb_buffers == 0 || fd->nb_buffers > 1024) { | 	if (fd->nb_buffers == 0 || fd->nb_buffers > 1024) { | ||||||
|  | 		ao_base_finish(&fd->base); | ||||||
| 		g_set_error(error_r, ffado_output_quark(), 0, | 		g_set_error(error_r, ffado_output_quark(), 0, | ||||||
| 			    "invalid nb_buffers setting"); | 			    "invalid nb_buffers setting"); | ||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return fd; | 	return &fd->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| ffado_finish(void *data) | ffado_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct mpd_ffado_device *fd = data; | 	struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao; | ||||||
|  |  | ||||||
| 	g_free(fd->device_name); | 	g_free(fd->device_name); | ||||||
|  | 	ao_base_finish(&fd->base); | ||||||
| 	g_free(fd); | 	g_free(fd); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -228,9 +237,10 @@ ffado_configure(struct mpd_ffado_device *fd, struct audio_format *audio_format, | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| ffado_open(void *data, struct audio_format *audio_format, GError **error_r) | ffado_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
|  | 	   GError **error_r) | ||||||
| { | { | ||||||
| 	struct mpd_ffado_device *fd = data; | 	struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao; | ||||||
|  |  | ||||||
| 	/* will be converted to floating point, choose best input | 	/* will be converted to floating point, choose best input | ||||||
| 	   format */ | 	   format */ | ||||||
| @@ -274,9 +284,9 @@ ffado_open(void *data, struct audio_format *audio_format, GError **error_r) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| ffado_close(void *data) | ffado_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct mpd_ffado_device *fd = data; | 	struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao; | ||||||
|  |  | ||||||
| 	ffado_streaming_stop(fd->dev); | 	ffado_streaming_stop(fd->dev); | ||||||
| 	ffado_streaming_finish(fd->dev); | 	ffado_streaming_finish(fd->dev); | ||||||
| @@ -288,9 +298,10 @@ ffado_close(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| ffado_play(void *data, const void *chunk, size_t size, GError **error_r) | ffado_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
|  | 	   GError **error_r) | ||||||
| { | { | ||||||
| 	struct mpd_ffado_device *fd = data; | 	struct mpd_ffado_device *fd = (struct mpd_ffado_device *)ao; | ||||||
|  |  | ||||||
| 	/* wait for prefious buffer to finish (if it was full) */ | 	/* wait for prefious buffer to finish (if it was full) */ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -39,6 +39,8 @@ | |||||||
| #define FIFO_BUFFER_SIZE 65536 /* pipe capacity on Linux >= 2.6.11 */ | #define FIFO_BUFFER_SIZE 65536 /* pipe capacity on Linux >= 2.6.11 */ | ||||||
|  |  | ||||||
| struct fifo_data { | struct fifo_data { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	char *path; | 	char *path; | ||||||
| 	int input; | 	int input; | ||||||
| 	int output; | 	int output; | ||||||
| @@ -176,9 +178,8 @@ fifo_open(struct fifo_data *fd, GError **error) | |||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| fifo_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | fifo_output_init(const struct config_param *param, | ||||||
| 		 const struct config_param *param, |  | ||||||
| 		 GError **error_r) | 		 GError **error_r) | ||||||
| { | { | ||||||
| 	struct fifo_data *fd; | 	struct fifo_data *fd; | ||||||
| @@ -197,28 +198,35 @@ fifo_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 	fd = fifo_data_new(); | 	fd = fifo_data_new(); | ||||||
| 	fd->path = path; | 	fd->path = path; | ||||||
|  |  | ||||||
| 	if (!fifo_open(fd, error_r)) { | 	if (!ao_base_init(&fd->base, &fifo_output_plugin, param, error_r)) { | ||||||
| 		fifo_data_free(fd); | 		fifo_data_free(fd); | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return fd; | 	if (!fifo_open(fd, error_r)) { | ||||||
|  | 		ao_base_finish(&fd->base); | ||||||
|  | 		fifo_data_free(fd); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &fd->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| fifo_output_finish(void *data) | fifo_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct fifo_data *fd = (struct fifo_data *)data; | 	struct fifo_data *fd = (struct fifo_data *)ao; | ||||||
|  |  | ||||||
| 	fifo_close(fd); | 	fifo_close(fd); | ||||||
|  | 	ao_base_finish(&fd->base); | ||||||
| 	fifo_data_free(fd); | 	fifo_data_free(fd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| fifo_output_open(void *data, struct audio_format *audio_format, | fifo_output_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
| 		 G_GNUC_UNUSED GError **error) | 		 G_GNUC_UNUSED GError **error) | ||||||
| { | { | ||||||
| 	struct fifo_data *fd = (struct fifo_data *)data; | 	struct fifo_data *fd = (struct fifo_data *)ao; | ||||||
|  |  | ||||||
| 	fd->timer = timer_new(audio_format); | 	fd->timer = timer_new(audio_format); | ||||||
|  |  | ||||||
| @@ -226,17 +234,17 @@ fifo_output_open(void *data, struct audio_format *audio_format, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| fifo_output_close(void *data) | fifo_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct fifo_data *fd = (struct fifo_data *)data; | 	struct fifo_data *fd = (struct fifo_data *)ao; | ||||||
|  |  | ||||||
| 	timer_free(fd->timer); | 	timer_free(fd->timer); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| fifo_output_cancel(void *data) | fifo_output_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct fifo_data *fd = (struct fifo_data *)data; | 	struct fifo_data *fd = (struct fifo_data *)ao; | ||||||
| 	char buf[FIFO_BUFFER_SIZE]; | 	char buf[FIFO_BUFFER_SIZE]; | ||||||
| 	int bytes = 1; | 	int bytes = 1; | ||||||
|  |  | ||||||
| @@ -252,10 +260,10 @@ fifo_output_cancel(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| fifo_output_play(void *data, const void *chunk, size_t size, | fifo_output_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
| 		 GError **error) | 		 GError **error) | ||||||
| { | { | ||||||
| 	struct fifo_data *fd = (struct fifo_data *)data; | 	struct fifo_data *fd = (struct fifo_data *)ao; | ||||||
| 	ssize_t bytes; | 	ssize_t bytes; | ||||||
|  |  | ||||||
| 	if (!fd->timer->started) | 	if (!fd->timer->started) | ||||||
| @@ -274,7 +282,7 @@ fifo_output_play(void *data, const void *chunk, size_t size, | |||||||
| 			switch (errno) { | 			switch (errno) { | ||||||
| 			case EAGAIN: | 			case EAGAIN: | ||||||
| 				/* The pipe is full, so empty it */ | 				/* The pipe is full, so empty it */ | ||||||
| 				fifo_output_cancel(fd); | 				fifo_output_cancel(&fd->base); | ||||||
| 				continue; | 				continue; | ||||||
| 			case EINTR: | 			case EINTR: | ||||||
| 				continue; | 				continue; | ||||||
|   | |||||||
| @@ -25,6 +25,7 @@ | |||||||
| #ifndef MPD_OUTPUT_HTTPD_INTERNAL_H | #ifndef MPD_OUTPUT_HTTPD_INTERNAL_H | ||||||
| #define MPD_OUTPUT_HTTPD_INTERNAL_H | #define MPD_OUTPUT_HTTPD_INTERNAL_H | ||||||
|  |  | ||||||
|  | #include "output_internal.h" | ||||||
| #include "timer.h" | #include "timer.h" | ||||||
|  |  | ||||||
| #include <glib.h> | #include <glib.h> | ||||||
| @@ -34,6 +35,8 @@ | |||||||
| struct httpd_client; | struct httpd_client; | ||||||
|  |  | ||||||
| struct httpd_output { | struct httpd_output { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * True if the audio output is open and accepts client | 	 * True if the audio output is open and accepts client | ||||||
| 	 * connections. | 	 * connections. | ||||||
|   | |||||||
| @@ -79,12 +79,16 @@ httpd_output_unbind(struct httpd_output *httpd) | |||||||
| 	g_mutex_unlock(httpd->mutex); | 	g_mutex_unlock(httpd->mutex); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | httpd_output_init(const struct config_param *param, | ||||||
| 		  const struct config_param *param, |  | ||||||
| 		  GError **error) | 		  GError **error) | ||||||
| { | { | ||||||
| 	struct httpd_output *httpd = g_new(struct httpd_output, 1); | 	struct httpd_output *httpd = g_new(struct httpd_output, 1); | ||||||
|  | 	if (!ao_base_init(&httpd->base, &httpd_output_plugin, param, error)) { | ||||||
|  | 		g_free(httpd); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	const char *encoder_name, *bind_to_address; | 	const char *encoder_name, *bind_to_address; | ||||||
| 	const struct encoder_plugin *encoder_plugin; | 	const struct encoder_plugin *encoder_plugin; | ||||||
| 	guint port; | 	guint port; | ||||||
| @@ -104,6 +108,7 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 	if (encoder_plugin == NULL) { | 	if (encoder_plugin == NULL) { | ||||||
| 		g_set_error(error, httpd_output_quark(), 0, | 		g_set_error(error, httpd_output_quark(), 0, | ||||||
| 			    "No such encoder: %s", encoder_name); | 			    "No such encoder: %s", encoder_name); | ||||||
|  | 		ao_base_finish(&httpd->base); | ||||||
| 		g_free(httpd); | 		g_free(httpd); | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
| @@ -121,8 +126,11 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 		? server_socket_add_host(httpd->server_socket, bind_to_address, | 		? server_socket_add_host(httpd->server_socket, bind_to_address, | ||||||
| 					 port, error) | 					 port, error) | ||||||
| 		: server_socket_add_port(httpd->server_socket, port, error); | 		: server_socket_add_port(httpd->server_socket, port, error); | ||||||
| 	if (!success) | 	if (!success) { | ||||||
|  | 		ao_base_finish(&httpd->base); | ||||||
|  | 		g_free(httpd); | ||||||
| 		return NULL; | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	/* initialize metadata */ | 	/* initialize metadata */ | ||||||
| 	httpd->metadata = NULL; | 	httpd->metadata = NULL; | ||||||
| @@ -131,8 +139,11 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 	/* initialize encoder */ | 	/* initialize encoder */ | ||||||
|  |  | ||||||
| 	httpd->encoder = encoder_init(encoder_plugin, param, error); | 	httpd->encoder = encoder_init(encoder_plugin, param, error); | ||||||
| 	if (httpd->encoder == NULL) | 	if (httpd->encoder == NULL) { | ||||||
|  | 		ao_base_finish(&httpd->base); | ||||||
|  | 		g_free(httpd); | ||||||
| 		return NULL; | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	/* determine content type */ | 	/* determine content type */ | ||||||
| 	httpd->content_type = encoder_get_mime_type(httpd->encoder); | 	httpd->content_type = encoder_get_mime_type(httpd->encoder); | ||||||
| @@ -142,13 +153,13 @@ httpd_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
|  |  | ||||||
| 	httpd->mutex = g_mutex_new(); | 	httpd->mutex = g_mutex_new(); | ||||||
|  |  | ||||||
| 	return httpd; | 	return &httpd->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| httpd_output_finish(void *data) | httpd_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct httpd_output *httpd = data; | 	struct httpd_output *httpd = (struct httpd_output *)ao; | ||||||
|  |  | ||||||
| 	if (httpd->metadata) | 	if (httpd->metadata) | ||||||
| 		page_unref(httpd->metadata); | 		page_unref(httpd->metadata); | ||||||
| @@ -156,6 +167,7 @@ httpd_output_finish(void *data) | |||||||
| 	encoder_finish(httpd->encoder); | 	encoder_finish(httpd->encoder); | ||||||
| 	server_socket_free(httpd->server_socket); | 	server_socket_free(httpd->server_socket); | ||||||
| 	g_mutex_free(httpd->mutex); | 	g_mutex_free(httpd->mutex); | ||||||
|  | 	ao_base_finish(&httpd->base); | ||||||
| 	g_free(httpd); | 	g_free(httpd); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -287,26 +299,26 @@ httpd_output_encoder_open(struct httpd_output *httpd, | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| httpd_output_enable(void *data, GError **error_r) | httpd_output_enable(struct audio_output *ao, GError **error_r) | ||||||
| { | { | ||||||
| 	struct httpd_output *httpd = data; | 	struct httpd_output *httpd = (struct httpd_output *)ao; | ||||||
|  |  | ||||||
| 	return httpd_output_bind(httpd, error_r); | 	return httpd_output_bind(httpd, error_r); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| httpd_output_disable(void *data) | httpd_output_disable(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct httpd_output *httpd = data; | 	struct httpd_output *httpd = (struct httpd_output *)ao; | ||||||
|  |  | ||||||
| 	httpd_output_unbind(httpd); | 	httpd_output_unbind(httpd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| httpd_output_open(void *data, struct audio_format *audio_format, | httpd_output_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
| 		  GError **error) | 		  GError **error) | ||||||
| { | { | ||||||
| 	struct httpd_output *httpd = data; | 	struct httpd_output *httpd = (struct httpd_output *)ao; | ||||||
| 	bool success; | 	bool success; | ||||||
|  |  | ||||||
| 	g_mutex_lock(httpd->mutex); | 	g_mutex_lock(httpd->mutex); | ||||||
| @@ -339,9 +351,10 @@ httpd_client_delete(gpointer data, G_GNUC_UNUSED gpointer user_data) | |||||||
| 	httpd_client_free(client); | 	httpd_client_free(client); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void httpd_output_close(void *data) | static void | ||||||
|  | httpd_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct httpd_output *httpd = data; | 	struct httpd_output *httpd = (struct httpd_output *)ao; | ||||||
|  |  | ||||||
| 	g_mutex_lock(httpd->mutex); | 	g_mutex_lock(httpd->mutex); | ||||||
|  |  | ||||||
| @@ -380,9 +393,9 @@ httpd_output_send_header(struct httpd_output *httpd, | |||||||
| } | } | ||||||
|  |  | ||||||
| static unsigned | static unsigned | ||||||
| httpd_output_delay(void *data) | httpd_output_delay(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct httpd_output *httpd = data; | 	struct httpd_output *httpd = (struct httpd_output *)ao; | ||||||
|  |  | ||||||
| 	return httpd->timer->started | 	return httpd->timer->started | ||||||
| 		? timer_delay(httpd->timer) | 		? timer_delay(httpd->timer) | ||||||
| @@ -458,9 +471,10 @@ httpd_output_encode_and_play(struct httpd_output *httpd, | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| httpd_output_play(void *data, const void *chunk, size_t size, GError **error) | httpd_output_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
|  | 		  GError **error) | ||||||
| { | { | ||||||
| 	struct httpd_output *httpd = data; | 	struct httpd_output *httpd = (struct httpd_output *)ao; | ||||||
| 	bool has_clients; | 	bool has_clients; | ||||||
|  |  | ||||||
| 	g_mutex_lock(httpd->mutex); | 	g_mutex_lock(httpd->mutex); | ||||||
| @@ -484,9 +498,9 @@ httpd_output_play(void *data, const void *chunk, size_t size, GError **error) | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| httpd_output_pause(void *data) | httpd_output_pause(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct httpd_output *httpd = data; | 	struct httpd_output *httpd = (struct httpd_output *)ao; | ||||||
|  |  | ||||||
| 	g_mutex_lock(httpd->mutex); | 	g_mutex_lock(httpd->mutex); | ||||||
| 	bool has_clients = httpd->clients != NULL; | 	bool has_clients = httpd->clients != NULL; | ||||||
| @@ -494,7 +508,7 @@ httpd_output_pause(void *data) | |||||||
|  |  | ||||||
| 	if (has_clients) { | 	if (has_clients) { | ||||||
| 		static const char silence[1020]; | 		static const char silence[1020]; | ||||||
| 		return httpd_output_play(data, silence, sizeof(silence), | 		return httpd_output_play(ao, silence, sizeof(silence), | ||||||
| 					 NULL) > 0; | 					 NULL) > 0; | ||||||
| 	} else { | 	} else { | ||||||
| 		g_usleep(100000); | 		g_usleep(100000); | ||||||
| @@ -512,9 +526,9 @@ httpd_send_metadata(gpointer data, gpointer user_data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| httpd_output_tag(void *data, const struct tag *tag) | httpd_output_tag(struct audio_output *ao, const struct tag *tag) | ||||||
| { | { | ||||||
| 	struct httpd_output *httpd = data; | 	struct httpd_output *httpd = (struct httpd_output *)ao; | ||||||
|  |  | ||||||
| 	assert(tag != NULL); | 	assert(tag != NULL); | ||||||
|  |  | ||||||
| @@ -571,9 +585,9 @@ httpd_client_cancel_callback(gpointer data, G_GNUC_UNUSED gpointer user_data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| httpd_output_cancel(void *data) | httpd_output_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct httpd_output *httpd = data; | 	struct httpd_output *httpd = (struct httpd_output *)ao; | ||||||
|  |  | ||||||
| 	g_mutex_lock(httpd->mutex); | 	g_mutex_lock(httpd->mutex); | ||||||
| 	g_list_foreach(httpd->clients, httpd_client_cancel_callback, NULL); | 	g_list_foreach(httpd->clients, httpd_client_cancel_callback, NULL); | ||||||
|   | |||||||
| @@ -44,6 +44,8 @@ enum { | |||||||
| static const size_t jack_sample_size = sizeof(jack_default_audio_sample_t); | static const size_t jack_sample_size = sizeof(jack_default_audio_sample_t); | ||||||
|  |  | ||||||
| struct jack_data { | struct jack_data { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * libjack options passed to jack_client_open(). | 	 * libjack options passed to jack_client_open(). | ||||||
| 	 */ | 	 */ | ||||||
| @@ -292,14 +294,18 @@ parse_port_list(int line, const char *source, char **dest, GError **error_r) | |||||||
| 	return n; | 	return n; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| mpd_jack_init(G_GNUC_UNUSED const struct audio_format *audio_format, | mpd_jack_init(const struct config_param *param, GError **error_r) | ||||||
| 	      const struct config_param *param, GError **error_r) |  | ||||||
| { | { | ||||||
| 	struct jack_data *jd; | 	struct jack_data *jd = g_new(struct jack_data, 1); | ||||||
|  |  | ||||||
|  | 	if (!ao_base_init(&jd->base, &jack_output_plugin, param, error_r)) { | ||||||
|  | 		g_free(jd); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	const char *value; | 	const char *value; | ||||||
|  |  | ||||||
| 	jd = g_new(struct jack_data, 1); |  | ||||||
| 	jd->options = JackNullOption; | 	jd->options = JackNullOption; | ||||||
|  |  | ||||||
| 	jd->name = config_get_block_string(param, "client_name", NULL); | 	jd->name = config_get_block_string(param, "client_name", NULL); | ||||||
| @@ -362,13 +368,13 @@ mpd_jack_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 	jack_set_info_function(mpd_jack_info); | 	jack_set_info_function(mpd_jack_info); | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
| 	return jd; | 	return &jd->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| mpd_jack_finish(void *data) | mpd_jack_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct jack_data *jd = data; | 	struct jack_data *jd = (struct jack_data *)ao; | ||||||
|  |  | ||||||
| 	for (unsigned i = 0; i < jd->num_source_ports; ++i) | 	for (unsigned i = 0; i < jd->num_source_ports; ++i) | ||||||
| 		g_free(jd->source_ports[i]); | 		g_free(jd->source_ports[i]); | ||||||
| @@ -376,13 +382,14 @@ mpd_jack_finish(void *data) | |||||||
| 	for (unsigned i = 0; i < jd->num_destination_ports; ++i) | 	for (unsigned i = 0; i < jd->num_destination_ports; ++i) | ||||||
| 		g_free(jd->destination_ports[i]); | 		g_free(jd->destination_ports[i]); | ||||||
|  |  | ||||||
|  | 	ao_base_finish(&jd->base); | ||||||
| 	g_free(jd); | 	g_free(jd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| mpd_jack_enable(void *data, GError **error_r) | mpd_jack_enable(struct audio_output *ao, GError **error_r) | ||||||
| { | { | ||||||
| 	struct jack_data *jd = (struct jack_data *)data; | 	struct jack_data *jd = (struct jack_data *)ao; | ||||||
|  |  | ||||||
| 	for (unsigned i = 0; i < jd->num_source_ports; ++i) | 	for (unsigned i = 0; i < jd->num_source_ports; ++i) | ||||||
| 		jd->ringbuffer[i] = NULL; | 		jd->ringbuffer[i] = NULL; | ||||||
| @@ -391,9 +398,9 @@ mpd_jack_enable(void *data, GError **error_r) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| mpd_jack_disable(void *data) | mpd_jack_disable(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct jack_data *jd = (struct jack_data *)data; | 	struct jack_data *jd = (struct jack_data *)ao; | ||||||
|  |  | ||||||
| 	if (jd->client != NULL) | 	if (jd->client != NULL) | ||||||
| 		mpd_jack_disconnect(jd); | 		mpd_jack_disconnect(jd); | ||||||
| @@ -556,9 +563,10 @@ mpd_jack_start(struct jack_data *jd, GError **error_r) | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| mpd_jack_open(void *data, struct audio_format *audio_format, GError **error_r) | mpd_jack_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
|  | 	      GError **error_r) | ||||||
| { | { | ||||||
| 	struct jack_data *jd = data; | 	struct jack_data *jd = (struct jack_data *)ao; | ||||||
|  |  | ||||||
| 	assert(jd != NULL); | 	assert(jd != NULL); | ||||||
|  |  | ||||||
| @@ -577,9 +585,9 @@ mpd_jack_open(void *data, struct audio_format *audio_format, GError **error_r) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| mpd_jack_close(G_GNUC_UNUSED void *data) | mpd_jack_close(G_GNUC_UNUSED struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct jack_data *jd = data; | 	struct jack_data *jd = (struct jack_data *)ao; | ||||||
|  |  | ||||||
| 	mpd_jack_stop(jd); | 	mpd_jack_stop(jd); | ||||||
| } | } | ||||||
| @@ -649,9 +657,10 @@ mpd_jack_write_samples(struct jack_data *jd, const void *src, | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| mpd_jack_play(void *data, const void *chunk, size_t size, GError **error_r) | mpd_jack_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
|  | 	      GError **error_r) | ||||||
| { | { | ||||||
| 	struct jack_data *jd = data; | 	struct jack_data *jd = (struct jack_data *)ao; | ||||||
| 	const size_t frame_size = audio_format_frame_size(&jd->audio_format); | 	const size_t frame_size = audio_format_frame_size(&jd->audio_format); | ||||||
| 	size_t space = 0, space1; | 	size_t space = 0, space1; | ||||||
|  |  | ||||||
| @@ -693,9 +702,9 @@ mpd_jack_play(void *data, const void *chunk, size_t size, GError **error_r) | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| mpd_jack_pause(void *data) | mpd_jack_pause(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct jack_data *jd = data; | 	struct jack_data *jd = (struct jack_data *)ao; | ||||||
|  |  | ||||||
| 	if (jd->shutdown) | 	if (jd->shutdown) | ||||||
| 		return false; | 		return false; | ||||||
|   | |||||||
| @@ -70,6 +70,8 @@ typedef struct { | |||||||
| #define MVP_GET_AUD_REGS		_IOW('a',28,aud_ctl_regs_t*) | #define MVP_GET_AUD_REGS		_IOW('a',28,aud_ctl_regs_t*) | ||||||
|  |  | ||||||
| struct mvp_data { | struct mvp_data { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	struct audio_format audio_format; | 	struct audio_format audio_format; | ||||||
| 	int fd; | 	int fd; | ||||||
| }; | }; | ||||||
| @@ -131,21 +133,26 @@ mvp_output_test_default_device(void) | |||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| mvp_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | mvp_output_init(G_GNUC_UNUSED const struct config_param *param, GError **error) | ||||||
| 		G_GNUC_UNUSED const struct config_param *param, |  | ||||||
| 		G_GNUC_UNUSED GError **error) |  | ||||||
| { | { | ||||||
| 	struct mvp_data *md = g_new(struct mvp_data, 1); | 	struct mvp_data *md = g_new(struct mvp_data, 1); | ||||||
|  |  | ||||||
|  | 	if (!ao_base_init(&md->base, &mvp_output_plugin, param, error)) { | ||||||
|  | 		g_free(md); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	md->fd = -1; | 	md->fd = -1; | ||||||
|  |  | ||||||
| 	return md; | 	return &md->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| mvp_output_finish(void *data) | mvp_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct mvp_data *md = data; | 	struct mvp_data *md = (struct mvp_data *)ao; | ||||||
|  | 	ao_base_finish(&md->base); | ||||||
| 	g_free(md); | 	g_free(md); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -226,9 +233,10 @@ mvp_set_pcm_params(struct mvp_data *md, struct audio_format *audio_format, | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| mvp_output_open(void *data, struct audio_format *audio_format, GError **error) | mvp_output_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
|  | 		GError **error) | ||||||
| { | { | ||||||
| 	struct mvp_data *md = data; | 	struct mvp_data *md = (struct mvp_data *)ao; | ||||||
| 	long long int stc = 0; | 	long long int stc = 0; | ||||||
| 	int mix[5] = { 0, 2, 7, 1, 0 }; | 	int mix[5] = { 0, 2, 7, 1, 0 }; | ||||||
| 	bool success; | 	bool success; | ||||||
| @@ -274,17 +282,17 @@ mvp_output_open(void *data, struct audio_format *audio_format, GError **error) | |||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void mvp_output_close(void *data) | static void mvp_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct mvp_data *md = data; | 	struct mvp_data *md = (struct mvp_data *)ao; | ||||||
| 	if (md->fd >= 0) | 	if (md->fd >= 0) | ||||||
| 		close(md->fd); | 		close(md->fd); | ||||||
| 	md->fd = -1; | 	md->fd = -1; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void mvp_output_cancel(void *data) | static void mvp_output_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct mvp_data *md = data; | 	struct mvp_data *md = (struct mvp_data *)ao; | ||||||
| 	if (md->fd >= 0) { | 	if (md->fd >= 0) { | ||||||
| 		ioctl(md->fd, MVP_SET_AUD_RESET, 0x11); | 		ioctl(md->fd, MVP_SET_AUD_RESET, 0x11); | ||||||
| 		close(md->fd); | 		close(md->fd); | ||||||
| @@ -293,16 +301,17 @@ static void mvp_output_cancel(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| mvp_output_play(void *data, const void *chunk, size_t size, GError **error) | mvp_output_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
|  | 		GError **error) | ||||||
| { | { | ||||||
| 	struct mvp_data *md = data; | 	struct mvp_data *md = (struct mvp_data *)ao; | ||||||
| 	ssize_t ret; | 	ssize_t ret; | ||||||
|  |  | ||||||
| 	/* reopen the device since it was closed by dropBufferedAudio */ | 	/* reopen the device since it was closed by dropBufferedAudio */ | ||||||
| 	if (md->fd < 0) { | 	if (md->fd < 0) { | ||||||
| 		bool success; | 		bool success; | ||||||
|  |  | ||||||
| 		success = mvp_output_open(md, &md->audio_format, error); | 		success = mvp_output_open(ao, &md->audio_format, error); | ||||||
| 		if (!success) | 		if (!success) | ||||||
| 			return 0; | 			return 0; | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -27,39 +27,45 @@ | |||||||
| #include <assert.h> | #include <assert.h> | ||||||
|  |  | ||||||
| struct null_data { | struct null_data { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	bool sync; | 	bool sync; | ||||||
|  |  | ||||||
| 	struct timer *timer; | 	struct timer *timer; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| null_init(G_GNUC_UNUSED const struct audio_format *audio_format, | null_init(const struct config_param *param, GError **error_r) | ||||||
| 	  G_GNUC_UNUSED const struct config_param *param, |  | ||||||
| 	  G_GNUC_UNUSED GError **error) |  | ||||||
| { | { | ||||||
| 	struct null_data *nd = g_new(struct null_data, 1); | 	struct null_data *nd = g_new(struct null_data, 1); | ||||||
|  |  | ||||||
|  | 	if (!ao_base_init(&nd->base, &null_output_plugin, param, error_r)) { | ||||||
|  | 		g_free(nd); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	nd->sync = config_get_block_bool(param, "sync", true); | 	nd->sync = config_get_block_bool(param, "sync", true); | ||||||
| 	nd->timer = NULL; | 	nd->timer = NULL; | ||||||
|  |  | ||||||
| 	return nd; | 	return &nd->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| null_finish(void *data) | null_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct null_data *nd = data; | 	struct null_data *nd = (struct null_data *)ao; | ||||||
|  |  | ||||||
| 	assert(nd->timer == NULL); | 	assert(nd->timer == NULL); | ||||||
|  |  | ||||||
|  | 	ao_base_finish(&nd->base); | ||||||
| 	g_free(nd); | 	g_free(nd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| null_open(void *data, struct audio_format *audio_format, | null_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
| 	  G_GNUC_UNUSED GError **error) | 	  G_GNUC_UNUSED GError **error) | ||||||
| { | { | ||||||
| 	struct null_data *nd = data; | 	struct null_data *nd = (struct null_data *)ao; | ||||||
|  |  | ||||||
| 	if (nd->sync) | 	if (nd->sync) | ||||||
| 		nd->timer = timer_new(audio_format); | 		nd->timer = timer_new(audio_format); | ||||||
| @@ -68,9 +74,9 @@ null_open(void *data, struct audio_format *audio_format, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| null_close(void *data) | null_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct null_data *nd = data; | 	struct null_data *nd = (struct null_data *)ao; | ||||||
|  |  | ||||||
| 	if (nd->timer != NULL) { | 	if (nd->timer != NULL) { | ||||||
| 		timer_free(nd->timer); | 		timer_free(nd->timer); | ||||||
| @@ -79,10 +85,10 @@ null_close(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| null_play(void *data, G_GNUC_UNUSED const void *chunk, size_t size, | null_play(struct audio_output *ao, G_GNUC_UNUSED const void *chunk, size_t size, | ||||||
| 	  G_GNUC_UNUSED GError **error) | 	  G_GNUC_UNUSED GError **error) | ||||||
| { | { | ||||||
| 	struct null_data *nd = data; | 	struct null_data *nd = (struct null_data *)ao; | ||||||
| 	struct timer *timer = nd->timer; | 	struct timer *timer = nd->timer; | ||||||
|  |  | ||||||
| 	if (!nd->sync) | 	if (!nd->sync) | ||||||
| @@ -99,9 +105,9 @@ null_play(void *data, G_GNUC_UNUSED const void *chunk, size_t size, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| null_cancel(void *data) | null_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct null_data *nd = data; | 	struct null_data *nd = (struct null_data *)ao; | ||||||
|  |  | ||||||
| 	if (!nd->sync) | 	if (!nd->sync) | ||||||
| 		return; | 		return; | ||||||
|   | |||||||
| @@ -39,6 +39,8 @@ | |||||||
| #define NUM_BUFFERS 16 | #define NUM_BUFFERS 16 | ||||||
|  |  | ||||||
| struct openal_data { | struct openal_data { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	const char *device_name; | 	const char *device_name; | ||||||
| 	ALCdevice *device; | 	ALCdevice *device; | ||||||
| 	ALCcontext *context; | 	ALCcontext *context; | ||||||
| @@ -126,10 +128,8 @@ openal_unqueue_buffers(struct openal_data *od) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| openal_init(G_GNUC_UNUSED const struct audio_format *audio_format, | openal_init(const struct config_param *param, GError **error_r) | ||||||
| 	    const struct config_param *param, |  | ||||||
| 	    G_GNUC_UNUSED GError **error) |  | ||||||
| { | { | ||||||
| 	const char *device_name = config_get_block_string(param, "device", NULL); | 	const char *device_name = config_get_block_string(param, "device", NULL); | ||||||
| 	struct openal_data *od; | 	struct openal_data *od; | ||||||
| @@ -139,24 +139,30 @@ openal_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	od = g_new(struct openal_data, 1); | 	od = g_new(struct openal_data, 1); | ||||||
|  | 	if (!ao_base_init(&od->base, &openal_output_plugin, param, error_r)) { | ||||||
|  | 		g_free(od); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	od->device_name = device_name; | 	od->device_name = device_name; | ||||||
|  |  | ||||||
| 	return od; | 	return &od->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| openal_finish(void *data) | openal_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct openal_data *od = data; | 	struct openal_data *od = (struct openal_data *)ao; | ||||||
|  |  | ||||||
|  | 	ao_base_finish(&od->base); | ||||||
| 	g_free(od); | 	g_free(od); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| openal_open(void *data, struct audio_format *audio_format, | openal_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
| 	    GError **error) | 	    GError **error) | ||||||
| { | { | ||||||
| 	struct openal_data *od = data; | 	struct openal_data *od = (struct openal_data *)ao; | ||||||
|  |  | ||||||
| 	od->format = openal_audio_format(audio_format); | 	od->format = openal_audio_format(audio_format); | ||||||
|  |  | ||||||
| @@ -198,9 +204,9 @@ openal_open(void *data, struct audio_format *audio_format, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| openal_close(void *data) | openal_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct openal_data *od = data; | 	struct openal_data *od = (struct openal_data *)ao; | ||||||
|  |  | ||||||
| 	timer_free(od->timer); | 	timer_free(od->timer); | ||||||
| 	alcMakeContextCurrent(od->context); | 	alcMakeContextCurrent(od->context); | ||||||
| @@ -211,10 +217,10 @@ openal_close(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| openal_play(void *data, const void *chunk, size_t size, | openal_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
| 	    G_GNUC_UNUSED GError **error) | 	    G_GNUC_UNUSED GError **error) | ||||||
| { | { | ||||||
| 	struct openal_data *od = data; | 	struct openal_data *od = (struct openal_data *)ao; | ||||||
| 	ALuint buffer; | 	ALuint buffer; | ||||||
| 	ALint num, state; | 	ALint num, state; | ||||||
|  |  | ||||||
| @@ -257,9 +263,9 @@ openal_play(void *data, const void *chunk, size_t size, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| openal_cancel(void *data) | openal_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct openal_data *od = data; | 	struct openal_data *od = (struct openal_data *)ao; | ||||||
|  |  | ||||||
| 	od->filled = 0; | 	od->filled = 0; | ||||||
| 	alcMakeContextCurrent(od->context); | 	alcMakeContextCurrent(od->context); | ||||||
|   | |||||||
| @@ -52,6 +52,8 @@ | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| struct oss_data { | struct oss_data { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	int fd; | 	int fd; | ||||||
| 	const char *device; | 	const char *device; | ||||||
|  |  | ||||||
| @@ -143,7 +145,7 @@ oss_output_test_default_device(void) | |||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| oss_open_default(GError **error) | oss_open_default(GError **error) | ||||||
| { | { | ||||||
| 	int i; | 	int i; | ||||||
| @@ -154,8 +156,14 @@ oss_open_default(GError **error) | |||||||
| 		ret[i] = oss_stat_device(default_devices[i], &err[i]); | 		ret[i] = oss_stat_device(default_devices[i], &err[i]); | ||||||
| 		if (ret[i] == OSS_STAT_NO_ERROR) { | 		if (ret[i] == OSS_STAT_NO_ERROR) { | ||||||
| 			struct oss_data *od = oss_data_new(); | 			struct oss_data *od = oss_data_new(); | ||||||
|  | 			if (!ao_base_init(&od->base, &oss_output_plugin, NULL, | ||||||
|  | 					  error)) { | ||||||
|  | 				g_free(od); | ||||||
|  | 				return NULL; | ||||||
|  | 			} | ||||||
|  |  | ||||||
| 			od->device = default_devices[i]; | 			od->device = default_devices[i]; | ||||||
| 			return od; | 			return &od->base; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -185,26 +193,31 @@ oss_open_default(GError **error) | |||||||
| 	return NULL; | 	return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| oss_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | oss_output_init(const struct config_param *param, GError **error) | ||||||
| 		const struct config_param *param, |  | ||||||
| 		GError **error) |  | ||||||
| { | { | ||||||
| 	const char *device = config_get_block_string(param, "device", NULL); | 	const char *device = config_get_block_string(param, "device", NULL); | ||||||
| 	if (device != NULL) { | 	if (device != NULL) { | ||||||
| 		struct oss_data *od = oss_data_new(); | 		struct oss_data *od = oss_data_new(); | ||||||
|  | 		if (!ao_base_init(&od->base, &oss_output_plugin, param, | ||||||
|  | 				  error)) { | ||||||
|  | 			g_free(od); | ||||||
|  | 			return NULL; | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		od->device = device; | 		od->device = device; | ||||||
| 		return od; | 		return &od->base; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return oss_open_default(error); | 	return oss_open_default(error); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| oss_output_finish(void *data) | oss_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct oss_data *od = data; | 	struct oss_data *od = (struct oss_data *)ao; | ||||||
|  |  | ||||||
|  | 	ao_base_finish(&od->base); | ||||||
| 	oss_data_free(od); | 	oss_data_free(od); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -607,9 +620,10 @@ oss_reopen(struct oss_data *od, GError **error_r) | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| oss_output_open(void *data, struct audio_format *audio_format, GError **error) | oss_output_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
|  | 		GError **error) | ||||||
| { | { | ||||||
| 	struct oss_data *od = data; | 	struct oss_data *od = (struct oss_data *)ao; | ||||||
|  |  | ||||||
| 	od->fd = open_cloexec(od->device, O_WRONLY, 0); | 	od->fd = open_cloexec(od->device, O_WRONLY, 0); | ||||||
| 	if (od->fd < 0) { | 	if (od->fd < 0) { | ||||||
| @@ -629,17 +643,17 @@ oss_output_open(void *data, struct audio_format *audio_format, GError **error) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| oss_output_close(void *data) | oss_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct oss_data *od = data; | 	struct oss_data *od = (struct oss_data *)ao; | ||||||
|  |  | ||||||
| 	oss_close(od); | 	oss_close(od); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| oss_output_cancel(void *data) | oss_output_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct oss_data *od = data; | 	struct oss_data *od = (struct oss_data *)ao; | ||||||
|  |  | ||||||
| 	if (od->fd >= 0) { | 	if (od->fd >= 0) { | ||||||
| 		ioctl(od->fd, SNDCTL_DSP_RESET, 0); | 		ioctl(od->fd, SNDCTL_DSP_RESET, 0); | ||||||
| @@ -648,9 +662,10 @@ oss_output_cancel(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| oss_output_play(void *data, const void *chunk, size_t size, GError **error) | oss_output_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
|  | 		GError **error) | ||||||
| { | { | ||||||
| 	struct oss_data *od = data; | 	struct oss_data *od = (struct oss_data *)ao; | ||||||
| 	ssize_t ret; | 	ssize_t ret; | ||||||
|  |  | ||||||
| 	/* reopen the device since it was closed by dropBufferedAudio */ | 	/* reopen the device since it was closed by dropBufferedAudio */ | ||||||
|   | |||||||
| @@ -29,6 +29,8 @@ | |||||||
| #define G_LOG_DOMAIN "osx" | #define G_LOG_DOMAIN "osx" | ||||||
|  |  | ||||||
| struct osx_output { | struct osx_output { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	/* configuration settings */ | 	/* configuration settings */ | ||||||
| 	OSType component_subtype; | 	OSType component_subtype; | ||||||
| 	/* only applicable with kAudioUnitSubType_HALOutput */ | 	/* only applicable with kAudioUnitSubType_HALOutput */ | ||||||
| @@ -80,12 +82,14 @@ osx_output_configure(struct osx_output *oo, const struct config_param *param) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | osx_output_init(const struct config_param *param, GError **error_r) | ||||||
| 		G_GNUC_UNUSED const struct config_param *param, |  | ||||||
| 		G_GNUC_UNUSED GError **error) |  | ||||||
| { | { | ||||||
| 	struct osx_output *oo = g_new(struct osx_output, 1); | 	struct osx_output *oo = g_new(struct osx_output, 1); | ||||||
|  | 	if (!ao_base_init(&oo->base, &osx_output_plugin, param, error_r)) { | ||||||
|  | 		g_free(oo); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	osx_output_configure(oo, param); | 	osx_output_configure(oo, param); | ||||||
| 	oo->mutex = g_mutex_new(); | 	oo->mutex = g_mutex_new(); | ||||||
| @@ -96,12 +100,13 @@ osx_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 	oo->buffer = NULL; | 	oo->buffer = NULL; | ||||||
| 	oo->buffer_size = 0; | 	oo->buffer_size = 0; | ||||||
|  |  | ||||||
| 	return oo; | 	return &oo->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void osx_output_finish(void *data) | static void | ||||||
|  | osx_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct osx_output *od = data; | 	struct osx_output *od = (struct osx_output *)ao; | ||||||
|  |  | ||||||
| 	g_free(od->buffer); | 	g_free(od->buffer); | ||||||
| 	g_mutex_free(od->mutex); | 	g_mutex_free(od->mutex); | ||||||
| @@ -109,18 +114,20 @@ static void osx_output_finish(void *data) | |||||||
| 	g_free(od); | 	g_free(od); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void osx_output_cancel(void *data) | static void | ||||||
|  | osx_output_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct osx_output *od = data; | 	struct osx_output *od = (struct osx_output *)ao; | ||||||
|  |  | ||||||
| 	g_mutex_lock(od->mutex); | 	g_mutex_lock(od->mutex); | ||||||
| 	od->len = 0; | 	od->len = 0; | ||||||
| 	g_mutex_unlock(od->mutex); | 	g_mutex_unlock(od->mutex); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void osx_output_close(void *data) | static void | ||||||
|  | osx_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct osx_output *od = data; | 	struct osx_output *od = (struct osx_output *)ao; | ||||||
|  |  | ||||||
| 	AudioOutputUnitStop(od->au); | 	AudioOutputUnitStop(od->au); | ||||||
| 	AudioUnitUninitialize(od->au); | 	AudioUnitUninitialize(od->au); | ||||||
| @@ -266,9 +273,9 @@ done: | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| osx_output_open(void *data, struct audio_format *audio_format, GError **error) | osx_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error) | ||||||
| { | { | ||||||
| 	struct osx_output *od = data; | 	struct osx_output *od = (struct osx_output *)ao; | ||||||
| 	ComponentDescription desc; | 	ComponentDescription desc; | ||||||
| 	Component comp; | 	Component comp; | ||||||
| 	AURenderCallbackStruct callback; | 	AURenderCallbackStruct callback; | ||||||
| @@ -385,10 +392,10 @@ osx_output_open(void *data, struct audio_format *audio_format, GError **error) | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| osx_output_play(void *data, const void *chunk, size_t size, | osx_output_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
| 		G_GNUC_UNUSED GError **error) | 		G_GNUC_UNUSED GError **error) | ||||||
| { | { | ||||||
| 	struct osx_output *od = data; | 	struct osx_output *od = (struct osx_output *)ao; | ||||||
| 	size_t start, nbytes; | 	size_t start, nbytes; | ||||||
|  |  | ||||||
| 	g_mutex_lock(od->mutex); | 	g_mutex_lock(od->mutex); | ||||||
|   | |||||||
| @@ -25,6 +25,8 @@ | |||||||
| #include <errno.h> | #include <errno.h> | ||||||
|  |  | ||||||
| struct pipe_output { | struct pipe_output { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	char *cmd; | 	char *cmd; | ||||||
| 	FILE *fh; | 	FILE *fh; | ||||||
| }; | }; | ||||||
| @@ -38,13 +40,17 @@ pipe_output_quark(void) | |||||||
| 	return g_quark_from_static_string("pipe_output"); | 	return g_quark_from_static_string("pipe_output"); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| pipe_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | pipe_output_init(const struct config_param *param, | ||||||
| 		 const struct config_param *param, |  | ||||||
| 		 GError **error) | 		 GError **error) | ||||||
| { | { | ||||||
| 	struct pipe_output *pd = g_new(struct pipe_output, 1); | 	struct pipe_output *pd = g_new(struct pipe_output, 1); | ||||||
|  |  | ||||||
|  | 	if (!ao_base_init(&pd->base, &pipe_output_plugin, param, error)) { | ||||||
|  | 		g_free(pd); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	pd->cmd = config_dup_block_string(param, "command", NULL); | 	pd->cmd = config_dup_block_string(param, "command", NULL); | ||||||
| 	if (pd->cmd == NULL) { | 	if (pd->cmd == NULL) { | ||||||
| 		g_set_error(error, pipe_output_quark(), 0, | 		g_set_error(error, pipe_output_quark(), 0, | ||||||
| @@ -52,23 +58,25 @@ pipe_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return pd; | 	return &pd->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| pipe_output_finish(void *data) | pipe_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct pipe_output *pd = data; | 	struct pipe_output *pd = (struct pipe_output *)ao; | ||||||
|  |  | ||||||
| 	g_free(pd->cmd); | 	g_free(pd->cmd); | ||||||
|  | 	ao_base_finish(&pd->base); | ||||||
| 	g_free(pd); | 	g_free(pd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| pipe_output_open(void *data, G_GNUC_UNUSED struct audio_format *audio_format, | pipe_output_open(struct audio_output *ao, | ||||||
|  | 		 G_GNUC_UNUSED struct audio_format *audio_format, | ||||||
| 		 G_GNUC_UNUSED GError **error) | 		 G_GNUC_UNUSED GError **error) | ||||||
| { | { | ||||||
| 	struct pipe_output *pd = data; | 	struct pipe_output *pd = (struct pipe_output *)ao; | ||||||
|  |  | ||||||
| 	pd->fh = popen(pd->cmd, "w"); | 	pd->fh = popen(pd->cmd, "w"); | ||||||
| 	if (pd->fh == NULL) { | 	if (pd->fh == NULL) { | ||||||
| @@ -82,17 +90,17 @@ pipe_output_open(void *data, G_GNUC_UNUSED struct audio_format *audio_format, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| pipe_output_close(void *data) | pipe_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct pipe_output *pd = data; | 	struct pipe_output *pd = (struct pipe_output *)ao; | ||||||
|  |  | ||||||
| 	pclose(pd->fh); | 	pclose(pd->fh); | ||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| pipe_output_play(void *data, const void *chunk, size_t size, GError **error) | pipe_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error) | ||||||
| { | { | ||||||
| 	struct pipe_output *pd = data; | 	struct pipe_output *pd = (struct pipe_output *)ao; | ||||||
| 	size_t ret; | 	size_t ret; | ||||||
|  |  | ||||||
| 	ret = fwrite(chunk, 1, size, pd->fh); | 	ret = fwrite(chunk, 1, size, pd->fh); | ||||||
|   | |||||||
| @@ -46,6 +46,8 @@ | |||||||
| #endif | #endif | ||||||
|  |  | ||||||
| struct pulse_output { | struct pulse_output { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	const char *name; | 	const char *name; | ||||||
| 	const char *server; | 	const char *server; | ||||||
| 	const char *sink; | 	const char *sink; | ||||||
| @@ -342,16 +344,19 @@ pulse_output_setup_context(struct pulse_output *po, GError **error_r) | |||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | pulse_output_init(const struct config_param *param, GError **error_r) | ||||||
| 		  const struct config_param *param, |  | ||||||
| 		  G_GNUC_UNUSED GError **error_r) |  | ||||||
| { | { | ||||||
| 	struct pulse_output *po; | 	struct pulse_output *po; | ||||||
|  |  | ||||||
| 	g_setenv("PULSE_PROP_media.role", "music", true); | 	g_setenv("PULSE_PROP_media.role", "music", true); | ||||||
|  |  | ||||||
| 	po = g_new(struct pulse_output, 1); | 	po = g_new(struct pulse_output, 1); | ||||||
|  | 	if (!ao_base_init(&po->base, &pulse_output_plugin, param, error_r)) { | ||||||
|  | 		g_free(po); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	po->name = config_get_block_string(param, "name", "mpd_pulse"); | 	po->name = config_get_block_string(param, "name", "mpd_pulse"); | ||||||
| 	po->server = config_get_block_string(param, "server", NULL); | 	po->server = config_get_block_string(param, "server", NULL); | ||||||
| 	po->sink = config_get_block_string(param, "sink", NULL); | 	po->sink = config_get_block_string(param, "sink", NULL); | ||||||
| @@ -361,21 +366,22 @@ pulse_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 	po->context = NULL; | 	po->context = NULL; | ||||||
| 	po->stream = NULL; | 	po->stream = NULL; | ||||||
|  |  | ||||||
| 	return po; | 	return &po->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| pulse_output_finish(void *data) | pulse_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct pulse_output *po = data; | 	struct pulse_output *po = (struct pulse_output *)ao; | ||||||
|  |  | ||||||
|  | 	ao_base_finish(&po->base); | ||||||
| 	g_free(po); | 	g_free(po); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| pulse_output_enable(void *data, GError **error_r) | pulse_output_enable(struct audio_output *ao, GError **error_r) | ||||||
| { | { | ||||||
| 	struct pulse_output *po = data; | 	struct pulse_output *po = (struct pulse_output *)ao; | ||||||
|  |  | ||||||
| 	assert(po->mainloop == NULL); | 	assert(po->mainloop == NULL); | ||||||
| 	assert(po->context == NULL); | 	assert(po->context == NULL); | ||||||
| @@ -419,9 +425,9 @@ pulse_output_enable(void *data, GError **error_r) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| pulse_output_disable(void *data) | pulse_output_disable(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct pulse_output *po = data; | 	struct pulse_output *po = (struct pulse_output *)ao; | ||||||
|  |  | ||||||
| 	assert(po->mainloop != NULL); | 	assert(po->mainloop != NULL); | ||||||
|  |  | ||||||
| @@ -573,10 +579,10 @@ pulse_output_setup_stream(struct pulse_output *po, const pa_sample_spec *ss, | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| pulse_output_open(void *data, struct audio_format *audio_format, | pulse_output_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
| 		  GError **error_r) | 		  GError **error_r) | ||||||
| { | { | ||||||
| 	struct pulse_output *po = data; | 	struct pulse_output *po = (struct pulse_output *)ao; | ||||||
| 	pa_sample_spec ss; | 	pa_sample_spec ss; | ||||||
| 	int error; | 	int error; | ||||||
|  |  | ||||||
| @@ -647,9 +653,9 @@ pulse_output_open(void *data, struct audio_format *audio_format, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| pulse_output_close(void *data) | pulse_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct pulse_output *po = data; | 	struct pulse_output *po = (struct pulse_output *)ao; | ||||||
| 	pa_operation *o; | 	pa_operation *o; | ||||||
|  |  | ||||||
| 	assert(po->mainloop != NULL); | 	assert(po->mainloop != NULL); | ||||||
| @@ -796,9 +802,10 @@ pulse_output_stream_pause(struct pulse_output *po, bool pause, | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r) | pulse_output_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
|  | 		  GError **error_r) | ||||||
| { | { | ||||||
| 	struct pulse_output *po = data; | 	struct pulse_output *po = (struct pulse_output *)ao; | ||||||
| 	int error; | 	int error; | ||||||
|  |  | ||||||
| 	assert(po->mainloop != NULL); | 	assert(po->mainloop != NULL); | ||||||
| @@ -866,9 +873,9 @@ pulse_output_play(void *data, const void *chunk, size_t size, GError **error_r) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| pulse_output_cancel(void *data) | pulse_output_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct pulse_output *po = data; | 	struct pulse_output *po = (struct pulse_output *)ao; | ||||||
| 	pa_operation *o; | 	pa_operation *o; | ||||||
|  |  | ||||||
| 	assert(po->mainloop != NULL); | 	assert(po->mainloop != NULL); | ||||||
| @@ -898,9 +905,9 @@ pulse_output_cancel(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| pulse_output_pause(void *data) | pulse_output_pause(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct pulse_output *po = data; | 	struct pulse_output *po = (struct pulse_output *)ao; | ||||||
| 	GError *error = NULL; | 	GError *error = NULL; | ||||||
|  |  | ||||||
| 	assert(po->mainloop != NULL); | 	assert(po->mainloop != NULL); | ||||||
| @@ -945,12 +952,12 @@ pulse_output_test_default_device(void) | |||||||
| 	struct pulse_output *po; | 	struct pulse_output *po; | ||||||
| 	bool success; | 	bool success; | ||||||
|  |  | ||||||
| 	po = pulse_output_init(NULL, NULL, NULL); | 	po = (struct pulse_output *)pulse_output_init(NULL, NULL); | ||||||
| 	if (po == NULL) | 	if (po == NULL) | ||||||
| 		return false; | 		return false; | ||||||
|  |  | ||||||
| 	success = pulse_output_wait_connection(po, NULL); | 	success = pulse_output_wait_connection(po, NULL); | ||||||
| 	pulse_output_finish(po); | 	pulse_output_finish(&po->base); | ||||||
|  |  | ||||||
| 	return success; | 	return success; | ||||||
| } | } | ||||||
|   | |||||||
| @@ -79,6 +79,8 @@ struct encrypt_data { | |||||||
| /*********************************************************************/ | /*********************************************************************/ | ||||||
|  |  | ||||||
| struct raop_data { | struct raop_data { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	struct rtspcl_data *rtspcl; | 	struct rtspcl_data *rtspcl; | ||||||
| 	const char *addr; // target host address | 	const char *addr; // target host address | ||||||
| 	short rtsp_port; | 	short rtsp_port; | ||||||
| @@ -209,9 +211,13 @@ raop_session_new(GError **error_r) | |||||||
| } | } | ||||||
|  |  | ||||||
| static struct raop_data * | static struct raop_data * | ||||||
| new_raop_data(GError **error_r) | new_raop_data(const struct config_param *param, GError **error_r) | ||||||
| { | { | ||||||
| 	struct raop_data *ret = g_new(struct raop_data, 1); | 	struct raop_data *ret = g_new(struct raop_data, 1); | ||||||
|  | 	if (!ao_base_init(&ret->base, &raop_output_plugin, param, error_r)) { | ||||||
|  | 		g_free(ret); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	ret->control_mutex = g_mutex_new(); | 	ret->control_mutex = g_mutex_new(); | ||||||
|  |  | ||||||
| @@ -223,6 +229,7 @@ new_raop_data(GError **error_r) | |||||||
| 	if (raop_session == NULL && | 	if (raop_session == NULL && | ||||||
| 	    (raop_session = raop_session_new(error_r)) == NULL) { | 	    (raop_session = raop_session_new(error_r)) == NULL) { | ||||||
| 		g_mutex_free(ret->control_mutex); | 		g_mutex_free(ret->control_mutex); | ||||||
|  | 		ao_base_finish(&ret->base); | ||||||
| 		g_free(ret); | 		g_free(ret); | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
| @@ -721,10 +728,8 @@ send_audio_data(int fd, GError **error_r) | |||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| raop_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | raop_output_init(const struct config_param *param, GError **error_r) | ||||||
| 		 G_GNUC_UNUSED const struct config_param *param, |  | ||||||
| 		 GError **error_r) |  | ||||||
| { | { | ||||||
| 	const char *host = config_get_block_string(param, "host", NULL); | 	const char *host = config_get_block_string(param, "host", NULL); | ||||||
| 	if (host == NULL) { | 	if (host == NULL) { | ||||||
| @@ -735,14 +740,14 @@ raop_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
|  |  | ||||||
| 	struct raop_data *rd; | 	struct raop_data *rd; | ||||||
|  |  | ||||||
| 	rd = new_raop_data(error_r); | 	rd = new_raop_data(param, error_r); | ||||||
| 	if (rd == NULL) | 	if (rd == NULL) | ||||||
| 		return NULL; | 		return NULL; | ||||||
|  |  | ||||||
| 	rd->addr = host; | 	rd->addr = host; | ||||||
| 	rd->rtsp_port = config_get_block_unsigned(param, "port", 5000); | 	rd->rtsp_port = config_get_block_unsigned(param, "port", 5000); | ||||||
| 	rd->volume = config_get_block_unsigned(param, "volume", 75); | 	rd->volume = config_get_block_unsigned(param, "volume", 75); | ||||||
| 	return rd; | 	return &rd->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| @@ -755,14 +760,15 @@ raop_set_volume_local(struct raop_data *rd, int volume, GError **error_r) | |||||||
|  |  | ||||||
|  |  | ||||||
| static void | static void | ||||||
| raop_output_finish(void *data) | raop_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct raop_data *rd = data; | 	struct raop_data *rd = (struct raop_data *)ao; | ||||||
|  |  | ||||||
| 	if (rd->rtspcl) | 	if (rd->rtspcl) | ||||||
| 		rtspcl_close(rd->rtspcl); | 		rtspcl_close(rd->rtspcl); | ||||||
|  |  | ||||||
| 	g_mutex_free(rd->control_mutex); | 	g_mutex_free(rd->control_mutex); | ||||||
|  | 	ao_base_finish(&rd->base); | ||||||
| 	g_free(rd); | 	g_free(rd); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -797,11 +803,11 @@ raop_set_volume(struct raop_data *rd, unsigned volume, GError **error_r) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| raop_output_cancel(void *data) | raop_output_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	//flush | 	//flush | ||||||
| 	struct key_data kd; | 	struct key_data kd; | ||||||
| 	struct raop_data *rd = (struct raop_data *) data; | 	struct raop_data *rd = (struct raop_data *)ao; | ||||||
| 	int flush_diff = 1; | 	int flush_diff = 1; | ||||||
|  |  | ||||||
| 	rd->started = 0; | 	rd->started = 0; | ||||||
| @@ -825,9 +831,9 @@ raop_output_cancel(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| raop_output_pause(void *data) | raop_output_pause(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct raop_data *rd = (struct raop_data *) data; | 	struct raop_data *rd = (struct raop_data *)ao; | ||||||
|  |  | ||||||
| 	rd->paused = true; | 	rd->paused = true; | ||||||
| 	return true; | 	return true; | ||||||
| @@ -870,10 +876,10 @@ raop_output_remove(struct raop_data *rd) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| raop_output_close(void *data) | raop_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	//teardown | 	//teardown | ||||||
| 	struct raop_data *rd = data; | 	struct raop_data *rd = (struct raop_data *)ao; | ||||||
|  |  | ||||||
| 	raop_output_remove(rd); | 	raop_output_remove(rd); | ||||||
|  |  | ||||||
| @@ -887,10 +893,10 @@ raop_output_close(void *data) | |||||||
|  |  | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| raop_output_open(void *data, struct audio_format *audio_format, GError **error_r) | raop_output_open(struct audio_output *ao, struct audio_format *audio_format, GError **error_r) | ||||||
| { | { | ||||||
| 	//setup, etc. | 	//setup, etc. | ||||||
| 	struct raop_data *rd = data; | 	struct raop_data *rd = (struct raop_data *)ao; | ||||||
|  |  | ||||||
| 	g_mutex_lock(raop_session->list_mutex); | 	g_mutex_lock(raop_session->list_mutex); | ||||||
| 	if (raop_session->raop_list == NULL) { | 	if (raop_session->raop_list == NULL) { | ||||||
| @@ -940,11 +946,11 @@ raop_output_open(void *data, struct audio_format *audio_format, GError **error_r | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| raop_output_play(void *data, const void *chunk, size_t size, | raop_output_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
| 		 GError **error_r) | 		 GError **error_r) | ||||||
| { | { | ||||||
| 	//raopcl_send_sample | 	//raopcl_send_sample | ||||||
| 	struct raop_data *rd = data; | 	struct raop_data *rd = (struct raop_data *)ao; | ||||||
| 	size_t rval = 0, orig_size = size; | 	size_t rval = 0, orig_size = size; | ||||||
|  |  | ||||||
| 	rd->paused = false; | 	rd->paused = false; | ||||||
|   | |||||||
| @@ -35,6 +35,8 @@ | |||||||
| #define G_LOG_DOMAIN "recorder" | #define G_LOG_DOMAIN "recorder" | ||||||
|  |  | ||||||
| struct recorder_output { | struct recorder_output { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * The configured encoder plugin. | 	 * The configured encoder plugin. | ||||||
| 	 */ | 	 */ | ||||||
| @@ -65,11 +67,16 @@ recorder_output_quark(void) | |||||||
| 	return g_quark_from_static_string("recorder_output"); | 	return g_quark_from_static_string("recorder_output"); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| recorder_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | recorder_output_init(const struct config_param *param, GError **error_r) | ||||||
| 		     const struct config_param *param, GError **error_r) |  | ||||||
| { | { | ||||||
| 	struct recorder_output *recorder = g_new(struct recorder_output, 1); | 	struct recorder_output *recorder = g_new(struct recorder_output, 1); | ||||||
|  | 	if (!ao_base_init(&recorder->base, &recorder_output_plugin, param, | ||||||
|  | 			  error_r)) { | ||||||
|  | 		g_free(recorder); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	const char *encoder_name; | 	const char *encoder_name; | ||||||
| 	const struct encoder_plugin *encoder_plugin; | 	const struct encoder_plugin *encoder_plugin; | ||||||
|  |  | ||||||
| @@ -96,19 +103,21 @@ recorder_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | |||||||
| 	if (recorder->encoder == NULL) | 	if (recorder->encoder == NULL) | ||||||
| 		goto failure; | 		goto failure; | ||||||
|  |  | ||||||
| 	return recorder; | 	return &recorder->base; | ||||||
|  |  | ||||||
| failure: | failure: | ||||||
|  | 	ao_base_finish(&recorder->base); | ||||||
| 	g_free(recorder); | 	g_free(recorder); | ||||||
| 	return NULL; | 	return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| recorder_output_finish(void *data) | recorder_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct recorder_output *recorder = data; | 	struct recorder_output *recorder = (struct recorder_output *)ao; | ||||||
|  |  | ||||||
| 	encoder_finish(recorder->encoder); | 	encoder_finish(recorder->encoder); | ||||||
|  | 	ao_base_finish(&recorder->base); | ||||||
| 	g_free(recorder); | 	g_free(recorder); | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -155,10 +164,11 @@ recorder_output_encoder_to_file(struct recorder_output *recorder, | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| recorder_output_open(void *data, struct audio_format *audio_format, | recorder_output_open(struct audio_output *ao, | ||||||
|  | 		     struct audio_format *audio_format, | ||||||
| 		     GError **error_r) | 		     GError **error_r) | ||||||
| { | { | ||||||
| 	struct recorder_output *recorder = data; | 	struct recorder_output *recorder = (struct recorder_output *)ao; | ||||||
| 	bool success; | 	bool success; | ||||||
|  |  | ||||||
| 	/* create the output file */ | 	/* create the output file */ | ||||||
| @@ -186,9 +196,9 @@ recorder_output_open(void *data, struct audio_format *audio_format, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| recorder_output_close(void *data) | recorder_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct recorder_output *recorder = data; | 	struct recorder_output *recorder = (struct recorder_output *)ao; | ||||||
|  |  | ||||||
| 	/* flush the encoder and write the rest to the file */ | 	/* flush the encoder and write the rest to the file */ | ||||||
|  |  | ||||||
| @@ -203,10 +213,10 @@ recorder_output_close(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| recorder_output_play(void *data, const void *chunk, size_t size, | recorder_output_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
| 		     GError **error_r) | 		     GError **error_r) | ||||||
| { | { | ||||||
| 	struct recorder_output *recorder = data; | 	struct recorder_output *recorder = (struct recorder_output *)ao; | ||||||
|  |  | ||||||
| 	return encoder_write(recorder->encoder, chunk, size, error_r) && | 	return encoder_write(recorder->encoder, chunk, size, error_r) && | ||||||
| 		recorder_output_encoder_to_file(recorder, error_r) | 		recorder_output_encoder_to_file(recorder, error_r) | ||||||
|   | |||||||
| @@ -38,6 +38,8 @@ | |||||||
|  |  | ||||||
| typedef struct roar | typedef struct roar | ||||||
| { | { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	roar_vs_t * vss; | 	roar_vs_t * vss; | ||||||
| 	int err; | 	int err; | ||||||
| 	char *host; | 	char *host; | ||||||
| @@ -114,34 +116,39 @@ roar_configure(struct roar * self, const struct config_param *param) | |||||||
| 		: ROAR_ROLE_MUSIC; | 		: ROAR_ROLE_MUSIC; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| roar_init(G_GNUC_UNUSED const struct audio_format *audio_format, | roar_init(const struct config_param *param, GError **error_r) | ||||||
| 		const struct config_param *param, |  | ||||||
| 		G_GNUC_UNUSED GError **error) |  | ||||||
| { | { | ||||||
| 	struct roar *self = g_new0(struct roar, 1); | 	struct roar *self = g_new0(struct roar, 1); | ||||||
|  |  | ||||||
|  | 	if (!ao_base_init(&self->base, &roar_output_plugin, param, error_r)) { | ||||||
|  | 		g_free(self); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	self->lock = g_mutex_new(); | 	self->lock = g_mutex_new(); | ||||||
| 	self->err = ROAR_ERROR_NONE; | 	self->err = ROAR_ERROR_NONE; | ||||||
| 	roar_configure(self, param); | 	roar_configure(self, param); | ||||||
| 	return self; | 	return &self->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| roar_finish(void *data) | roar_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	roar_t * self = data; | 	struct roar *self = (struct roar *)ao; | ||||||
|  |  | ||||||
| 	g_free(self->host); | 	g_free(self->host); | ||||||
| 	g_free(self->name); | 	g_free(self->name); | ||||||
| 	g_mutex_free(self->lock); | 	g_mutex_free(self->lock); | ||||||
|  |  | ||||||
|  | 	ao_base_finish(&self->base); | ||||||
| 	g_free(self); | 	g_free(self); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| roar_open(void *data, struct audio_format *audio_format, GError **error) | roar_open(struct audio_output *ao, struct audio_format *audio_format, GError **error) | ||||||
| { | { | ||||||
| 	roar_t * self = data; | 	struct roar *self = (struct roar *)ao; | ||||||
| 	g_mutex_lock(self->lock); | 	g_mutex_lock(self->lock); | ||||||
|  |  | ||||||
| 	if (roar_simple_connect(&(self->con), self->host, self->name) < 0) | 	if (roar_simple_connect(&(self->con), self->host, self->name) < 0) | ||||||
| @@ -205,9 +212,9 @@ roar_open(void *data, struct audio_format *audio_format, GError **error) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| roar_close(void *data) | roar_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	roar_t * self = data; | 	struct roar *self = (struct roar *)ao; | ||||||
| 	g_mutex_lock(self->lock); | 	g_mutex_lock(self->lock); | ||||||
| 	self->alive = false; | 	self->alive = false; | ||||||
|  |  | ||||||
| @@ -246,9 +253,9 @@ roar_cancel_locked(struct roar *self) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| roar_cancel(void *data) | roar_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	roar_t * self = data; | 	struct roar *self = (struct roar *)ao; | ||||||
|  |  | ||||||
| 	g_mutex_lock(self->lock); | 	g_mutex_lock(self->lock); | ||||||
| 	roar_cancel_locked(self); | 	roar_cancel_locked(self); | ||||||
| @@ -256,9 +263,9 @@ roar_cancel(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| roar_play(void *data, const void *chunk, size_t size, GError **error) | roar_play(struct audio_output *ao, const void *chunk, size_t size, GError **error) | ||||||
| { | { | ||||||
| 	struct roar * self = data; | 	struct roar *self = (struct roar *)ao; | ||||||
| 	ssize_t rc; | 	ssize_t rc; | ||||||
|  |  | ||||||
| 	if (self->vss == NULL) | 	if (self->vss == NULL) | ||||||
| @@ -323,9 +330,9 @@ roar_tag_convert(enum tag_type type, bool *is_uuid) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| roar_send_tag(void *data, const struct tag *meta) | roar_send_tag(struct audio_output *ao, const struct tag *meta) | ||||||
| { | { | ||||||
| 	struct roar * self = data; | 	struct roar *self = (struct roar *)ao; | ||||||
|  |  | ||||||
| 	if (self->vss == NULL) | 	if (self->vss == NULL) | ||||||
| 		return; | 		return; | ||||||
|   | |||||||
| @@ -42,6 +42,8 @@ struct shout_buffer { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| struct shout_data { | struct shout_data { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	shout_t *shout_conn; | 	shout_t *shout_conn; | ||||||
| 	shout_metadata_t *shout_meta; | 	shout_metadata_t *shout_meta; | ||||||
|  |  | ||||||
| @@ -108,9 +110,8 @@ static void free_shout_data(struct shout_data *sd) | |||||||
| 		}							\ | 		}							\ | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| my_shout_init_driver(const struct audio_format *audio_format, | my_shout_init_driver(const struct config_param *param, | ||||||
| 		     const struct config_param *param, |  | ||||||
| 		     GError **error) | 		     GError **error) | ||||||
| { | { | ||||||
| 	struct shout_data *sd; | 	struct shout_data *sd; | ||||||
| @@ -129,14 +130,22 @@ my_shout_init_driver(const struct audio_format *audio_format, | |||||||
| 	const struct block_param *block_param; | 	const struct block_param *block_param; | ||||||
| 	int public; | 	int public; | ||||||
|  |  | ||||||
| 	if (audio_format == NULL || | 	sd = new_shout_data(); | ||||||
| 	    !audio_format_fully_defined(audio_format)) { |  | ||||||
| 		g_set_error(error, shout_output_quark(), 0, | 	if (!ao_base_init(&sd->base, &shout_output_plugin, param, error)) { | ||||||
| 			    "Need full audio format specification"); | 		free_shout_data(sd); | ||||||
| 		return NULL; | 		return NULL; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sd = new_shout_data(); | 	const struct audio_format *audio_format = | ||||||
|  | 		&sd->base.config_audio_format; | ||||||
|  | 	if (!audio_format_fully_defined(audio_format)) { | ||||||
|  | 		g_set_error(error, shout_output_quark(), 0, | ||||||
|  | 			    "Need full audio format specification"); | ||||||
|  | 		ao_base_finish(&sd->base); | ||||||
|  | 		free_shout_data(sd); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if (shout_init_count == 0) | 	if (shout_init_count == 0) | ||||||
| 		shout_init(); | 		shout_init(); | ||||||
| @@ -307,9 +316,10 @@ my_shout_init_driver(const struct audio_format *audio_format, | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return sd; | 	return &sd->base; | ||||||
|  |  | ||||||
| failure: | failure: | ||||||
|  | 	ao_base_finish(&sd->base); | ||||||
| 	free_shout_data(sd); | 	free_shout_data(sd); | ||||||
| 	return NULL; | 	return NULL; | ||||||
| } | } | ||||||
| @@ -379,12 +389,14 @@ static void close_shout_conn(struct shout_data * sd) | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| static void my_shout_finish_driver(void *data) | static void | ||||||
|  | my_shout_finish_driver(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct shout_data *sd = (struct shout_data *)data; | 	struct shout_data *sd = (struct shout_data *)ao; | ||||||
|  |  | ||||||
| 	encoder_finish(sd->encoder); | 	encoder_finish(sd->encoder); | ||||||
|  |  | ||||||
|  | 	ao_base_finish(&sd->base); | ||||||
| 	free_shout_data(sd); | 	free_shout_data(sd); | ||||||
|  |  | ||||||
| 	shout_init_count--; | 	shout_init_count--; | ||||||
| @@ -393,17 +405,19 @@ static void my_shout_finish_driver(void *data) | |||||||
| 		shout_shutdown(); | 		shout_shutdown(); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void my_shout_drop_buffered_audio(void *data) | static void | ||||||
|  | my_shout_drop_buffered_audio(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	G_GNUC_UNUSED | 	G_GNUC_UNUSED | ||||||
| 	struct shout_data *sd = (struct shout_data *)data; | 	struct shout_data *sd = (struct shout_data *)ao; | ||||||
|  |  | ||||||
| 	/* needs to be implemented for shout */ | 	/* needs to be implemented for shout */ | ||||||
| } | } | ||||||
|  |  | ||||||
| static void my_shout_close_device(void *data) | static void | ||||||
|  | my_shout_close_device(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct shout_data *sd = (struct shout_data *)data; | 	struct shout_data *sd = (struct shout_data *)ao; | ||||||
|  |  | ||||||
| 	close_shout_conn(sd); | 	close_shout_conn(sd); | ||||||
| } | } | ||||||
| @@ -430,10 +444,10 @@ shout_connect(struct shout_data *sd, GError **error) | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| my_shout_open_device(void *data, struct audio_format *audio_format, | my_shout_open_device(struct audio_output *ao, struct audio_format *audio_format, | ||||||
| 		     GError **error) | 		     GError **error) | ||||||
| { | { | ||||||
| 	struct shout_data *sd = (struct shout_data *)data; | 	struct shout_data *sd = (struct shout_data *)ao; | ||||||
| 	bool ret; | 	bool ret; | ||||||
|  |  | ||||||
| 	ret = shout_connect(sd, error); | 	ret = shout_connect(sd, error); | ||||||
| @@ -453,9 +467,9 @@ my_shout_open_device(void *data, struct audio_format *audio_format, | |||||||
| } | } | ||||||
|  |  | ||||||
| static unsigned | static unsigned | ||||||
| my_shout_delay(void *data) | my_shout_delay(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct shout_data *sd = (struct shout_data *)data; | 	struct shout_data *sd = (struct shout_data *)ao; | ||||||
|  |  | ||||||
| 	int delay = shout_delay(sd->shout_conn); | 	int delay = shout_delay(sd->shout_conn); | ||||||
| 	if (delay < 0) | 	if (delay < 0) | ||||||
| @@ -465,9 +479,10 @@ my_shout_delay(void *data) | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| my_shout_play(void *data, const void *chunk, size_t size, GError **error) | my_shout_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
|  | 	      GError **error) | ||||||
| { | { | ||||||
| 	struct shout_data *sd = (struct shout_data *)data; | 	struct shout_data *sd = (struct shout_data *)ao; | ||||||
|  |  | ||||||
| 	return encoder_write(sd->encoder, chunk, size, error) && | 	return encoder_write(sd->encoder, chunk, size, error) && | ||||||
| 		write_page(sd, error) | 		write_page(sd, error) | ||||||
| @@ -476,11 +491,11 @@ my_shout_play(void *data, const void *chunk, size_t size, GError **error) | |||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| my_shout_pause(void *data) | my_shout_pause(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	static const char silence[1020]; | 	static const char silence[1020]; | ||||||
|  |  | ||||||
| 	return my_shout_play(data, silence, sizeof(silence), NULL); | 	return my_shout_play(ao, silence, sizeof(silence), NULL); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| @@ -509,10 +524,10 @@ shout_tag_to_metadata(const struct tag *tag, char *dest, size_t size) | |||||||
| 	snprintf(dest, size, "%s - %s", artist, title); | 	snprintf(dest, size, "%s - %s", artist, title); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void my_shout_set_tag(void *data, | static void my_shout_set_tag(struct audio_output *ao, | ||||||
| 			     const struct tag *tag) | 			     const struct tag *tag) | ||||||
| { | { | ||||||
| 	struct shout_data *sd = (struct shout_data *)data; | 	struct shout_data *sd = (struct shout_data *)ao; | ||||||
| 	bool ret; | 	bool ret; | ||||||
| 	GError *error = NULL; | 	GError *error = NULL; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -36,6 +36,8 @@ | |||||||
| #define G_LOG_DOMAIN "solaris_output" | #define G_LOG_DOMAIN "solaris_output" | ||||||
|  |  | ||||||
| struct solaris_output { | struct solaris_output { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	/* configuration */ | 	/* configuration */ | ||||||
| 	const char *device; | 	const char *device; | ||||||
|  |  | ||||||
| @@ -60,31 +62,35 @@ solaris_output_test_default_device(void) | |||||||
| 		access("/dev/audio", W_OK) == 0; | 		access("/dev/audio", W_OK) == 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| solaris_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | solaris_output_init(const struct config_param *param, GError **error_r) | ||||||
| 		    const struct config_param *param, |  | ||||||
| 		    G_GNUC_UNUSED GError **error) |  | ||||||
| { | { | ||||||
| 	struct solaris_output *so = g_new(struct solaris_output, 1); | 	struct solaris_output *so = g_new(struct solaris_output, 1); | ||||||
|  |  | ||||||
|  | 	if (!ao_base_init(&so->base, &solaris_output_plugin, param, error_r)) { | ||||||
|  | 		g_free(so); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	so->device = config_get_block_string(param, "device", "/dev/audio"); | 	so->device = config_get_block_string(param, "device", "/dev/audio"); | ||||||
|  |  | ||||||
| 	return so; | 	return &so->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| solaris_output_finish(void *data) | solaris_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct solaris_output *so = data; | 	struct solaris_output *so = (struct solaris_output *)ao; | ||||||
|  |  | ||||||
|  | 	ao_base_finish(&so->base); | ||||||
| 	g_free(so); | 	g_free(so); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| solaris_output_open(void *data, struct audio_format *audio_format, | solaris_output_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
| 		    GError **error) | 		    GError **error) | ||||||
| { | { | ||||||
| 	struct solaris_output *so = data; | 	struct solaris_output *so = (struct solaris_output *)ao; | ||||||
| 	struct audio_info info; | 	struct audio_info info; | ||||||
| 	int ret, flags; | 	int ret, flags; | ||||||
|  |  | ||||||
| @@ -135,17 +141,18 @@ solaris_output_open(void *data, struct audio_format *audio_format, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| solaris_output_close(void *data) | solaris_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct solaris_output *so = data; | 	struct solaris_output *so = (struct solaris_output *)ao; | ||||||
|  |  | ||||||
| 	close(so->fd); | 	close(so->fd); | ||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| solaris_output_play(void *data, const void *chunk, size_t size, GError **error) | solaris_output_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
|  | 		    GError **error) | ||||||
| { | { | ||||||
| 	struct solaris_output *so = data; | 	struct solaris_output *so = (struct solaris_output *)ao; | ||||||
| 	ssize_t nbytes; | 	ssize_t nbytes; | ||||||
|  |  | ||||||
| 	nbytes = write(so->fd, chunk, size); | 	nbytes = write(so->fd, chunk, size); | ||||||
| @@ -159,9 +166,9 @@ solaris_output_play(void *data, const void *chunk, size_t size, GError **error) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| solaris_output_cancel(void *data) | solaris_output_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct solaris_output *so = data; | 	struct solaris_output *so = (struct solaris_output *)ao; | ||||||
|  |  | ||||||
| 	ioctl(so->fd, I_FLUSH); | 	ioctl(so->fd, I_FLUSH); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -38,6 +38,8 @@ struct winmm_buffer { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| struct winmm_output { | struct winmm_output { | ||||||
|  | 	struct audio_output base; | ||||||
|  |  | ||||||
| 	UINT device_id; | 	UINT device_id; | ||||||
| 	HWAVEOUT handle; | 	HWAVEOUT handle; | ||||||
|  |  | ||||||
| @@ -101,30 +103,34 @@ get_device_id(const char *device_name) | |||||||
| 	return WAVE_MAPPER; | 	return WAVE_MAPPER; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void * | static struct audio_output * | ||||||
| winmm_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, | winmm_output_init(const struct config_param *param, GError **error_r) | ||||||
| 		  G_GNUC_UNUSED const struct config_param *param, |  | ||||||
| 		  G_GNUC_UNUSED GError **error) |  | ||||||
| { | { | ||||||
| 	struct winmm_output *wo = g_new(struct winmm_output, 1); | 	struct winmm_output *wo = g_new(struct winmm_output, 1); | ||||||
|  | 	if (!ao_base_init(&wo->base, &winmm_output_plugin, param, error_r)) { | ||||||
|  | 		g_free(wo); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	const char *device = config_get_block_string(param, "device", NULL); | 	const char *device = config_get_block_string(param, "device", NULL); | ||||||
| 	wo->device_id = get_device_id(device); | 	wo->device_id = get_device_id(device); | ||||||
| 	return wo; | 	return &wo->base; | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| winmm_output_finish(void *data) | winmm_output_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct winmm_output *wo = data; | 	struct winmm_output *wo = (struct winmm_output *)ao; | ||||||
|  |  | ||||||
|  | 	ao_base_finish(&wo->base); | ||||||
| 	g_free(wo); | 	g_free(wo); | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| winmm_output_open(void *data, struct audio_format *audio_format, | winmm_output_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
| 		  GError **error_r) | 		  GError **error_r) | ||||||
| { | { | ||||||
| 	struct winmm_output *wo = data; | 	struct winmm_output *wo = (struct winmm_output *)ao; | ||||||
|  |  | ||||||
| 	wo->event = CreateEvent(NULL, false, false, NULL); | 	wo->event = CreateEvent(NULL, false, false, NULL); | ||||||
| 	if (wo->event == NULL) { | 	if (wo->event == NULL) { | ||||||
| @@ -180,9 +186,9 @@ winmm_output_open(void *data, struct audio_format *audio_format, | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| winmm_output_close(void *data) | winmm_output_close(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct winmm_output *wo = data; | 	struct winmm_output *wo = (struct winmm_output *)ao; | ||||||
|  |  | ||||||
| 	for (unsigned i = 0; i < G_N_ELEMENTS(wo->buffers); ++i) | 	for (unsigned i = 0; i < G_N_ELEMENTS(wo->buffers); ++i) | ||||||
| 		pcm_buffer_deinit(&wo->buffers[i].buffer); | 		pcm_buffer_deinit(&wo->buffers[i].buffer); | ||||||
| @@ -253,9 +259,9 @@ winmm_drain_buffer(struct winmm_output *wo, struct winmm_buffer *buffer, | |||||||
| } | } | ||||||
|  |  | ||||||
| static size_t | static size_t | ||||||
| winmm_output_play(void *data, const void *chunk, size_t size, GError **error_r) | winmm_output_play(struct audio_output *ao, const void *chunk, size_t size, GError **error_r) | ||||||
| { | { | ||||||
| 	struct winmm_output *wo = data; | 	struct winmm_output *wo = (struct winmm_output *)ao; | ||||||
|  |  | ||||||
| 	/* get the next buffer from the ring and prepare it */ | 	/* get the next buffer from the ring and prepare it */ | ||||||
| 	struct winmm_buffer *buffer = &wo->buffers[wo->next_buffer]; | 	struct winmm_buffer *buffer = &wo->buffers[wo->next_buffer]; | ||||||
| @@ -308,18 +314,18 @@ winmm_stop(struct winmm_output *wo) | |||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| winmm_output_drain(void *data) | winmm_output_drain(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct winmm_output *wo = data; | 	struct winmm_output *wo = (struct winmm_output *)ao; | ||||||
|  |  | ||||||
| 	if (!winmm_drain_all_buffers(wo, NULL)) | 	if (!winmm_drain_all_buffers(wo, NULL)) | ||||||
| 		winmm_stop(wo); | 		winmm_stop(wo); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| winmm_output_cancel(void *data) | winmm_output_cancel(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	struct winmm_output *wo = data; | 	struct winmm_output *wo = (struct winmm_output *)ao; | ||||||
|  |  | ||||||
| 	winmm_stop(wo); | 	winmm_stop(wo); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ | |||||||
|  |  | ||||||
| static struct audio_format input_audio_format; | static struct audio_format input_audio_format; | ||||||
|  |  | ||||||
| static struct audio_output *audio_outputs; | static struct audio_output **audio_outputs; | ||||||
| static unsigned int num_audio_outputs; | static unsigned int num_audio_outputs; | ||||||
|  |  | ||||||
| /** | /** | ||||||
| @@ -70,7 +70,9 @@ audio_output_get(unsigned i) | |||||||
| { | { | ||||||
| 	assert(i < num_audio_outputs); | 	assert(i < num_audio_outputs); | ||||||
|  |  | ||||||
| 	return &audio_outputs[i]; | 	assert(audio_outputs[i] != NULL); | ||||||
|  |  | ||||||
|  | 	return audio_outputs[i]; | ||||||
| } | } | ||||||
|  |  | ||||||
| struct audio_output * | struct audio_output * | ||||||
| @@ -110,11 +112,10 @@ audio_output_all_init(struct player_control *pc) | |||||||
| 	notify_init(&audio_output_client_notify); | 	notify_init(&audio_output_client_notify); | ||||||
|  |  | ||||||
| 	num_audio_outputs = audio_output_config_count(); | 	num_audio_outputs = audio_output_config_count(); | ||||||
| 	audio_outputs = g_new(struct audio_output, num_audio_outputs); | 	audio_outputs = g_new(struct audio_output *, num_audio_outputs); | ||||||
|  |  | ||||||
| 	for (i = 0; i < num_audio_outputs; i++) | 	for (i = 0; i < num_audio_outputs; i++) | ||||||
| 	{ | 	{ | ||||||
| 		struct audio_output *output = &audio_outputs[i]; |  | ||||||
| 		unsigned int j; | 		unsigned int j; | ||||||
|  |  | ||||||
| 		param = config_get_next_param(CONF_AUDIO_OUTPUT, param); | 		param = config_get_next_param(CONF_AUDIO_OUTPUT, param); | ||||||
| @@ -122,7 +123,8 @@ audio_output_all_init(struct player_control *pc) | |||||||
| 		/* only allow param to be NULL if there just one audioOutput */ | 		/* only allow param to be NULL if there just one audioOutput */ | ||||||
| 		assert(param || (num_audio_outputs == 1)); | 		assert(param || (num_audio_outputs == 1)); | ||||||
|  |  | ||||||
| 		if (!audio_output_init(output, param, pc, &error)) { | 		struct audio_output *output = audio_output_new(param, pc, &error); | ||||||
|  | 		if (output == NULL) { | ||||||
| 			if (param != NULL) | 			if (param != NULL) | ||||||
| 				MPD_ERROR("line %i: %s", | 				MPD_ERROR("line %i: %s", | ||||||
| 					  param->line, error->message); | 					  param->line, error->message); | ||||||
| @@ -130,9 +132,11 @@ audio_output_all_init(struct player_control *pc) | |||||||
| 				MPD_ERROR("%s", error->message); | 				MPD_ERROR("%s", error->message); | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		audio_outputs[i] = output; | ||||||
|  |  | ||||||
| 		/* require output names to be unique: */ | 		/* require output names to be unique: */ | ||||||
| 		for (j = 0; j < i; j++) { | 		for (j = 0; j < i; j++) { | ||||||
| 			if (!strcmp(output->name, audio_outputs[j].name)) { | 			if (!strcmp(output->name, audio_outputs[j]->name)) { | ||||||
| 				MPD_ERROR("output devices with identical " | 				MPD_ERROR("output devices with identical " | ||||||
| 					  "names: %s\n", output->name); | 					  "names: %s\n", output->name); | ||||||
| 			} | 			} | ||||||
| @@ -146,8 +150,8 @@ audio_output_all_finish(void) | |||||||
| 	unsigned int i; | 	unsigned int i; | ||||||
|  |  | ||||||
| 	for (i = 0; i < num_audio_outputs; i++) { | 	for (i = 0; i < num_audio_outputs; i++) { | ||||||
| 		audio_output_disable(&audio_outputs[i]); | 		audio_output_disable(audio_outputs[i]); | ||||||
| 		audio_output_finish(&audio_outputs[i]); | 		audio_output_finish(audio_outputs[i]); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	g_free(audio_outputs); | 	g_free(audio_outputs); | ||||||
| @@ -161,7 +165,7 @@ void | |||||||
| audio_output_all_enable_disable(void) | audio_output_all_enable_disable(void) | ||||||
| { | { | ||||||
| 	for (unsigned i = 0; i < num_audio_outputs; i++) { | 	for (unsigned i = 0; i < num_audio_outputs; i++) { | ||||||
| 		struct audio_output *ao = &audio_outputs[i]; | 		struct audio_output *ao = audio_outputs[i]; | ||||||
| 		bool enabled; | 		bool enabled; | ||||||
|  |  | ||||||
| 		g_mutex_lock(ao->mutex); | 		g_mutex_lock(ao->mutex); | ||||||
| @@ -185,7 +189,7 @@ static bool | |||||||
| audio_output_all_finished(void) | audio_output_all_finished(void) | ||||||
| { | { | ||||||
| 	for (unsigned i = 0; i < num_audio_outputs; ++i) { | 	for (unsigned i = 0; i < num_audio_outputs; ++i) { | ||||||
| 		struct audio_output *ao = &audio_outputs[i]; | 		struct audio_output *ao = audio_outputs[i]; | ||||||
| 		bool not_finished; | 		bool not_finished; | ||||||
|  |  | ||||||
| 		g_mutex_lock(ao->mutex); | 		g_mutex_lock(ao->mutex); | ||||||
| @@ -213,7 +217,7 @@ static void | |||||||
| audio_output_allow_play_all(void) | audio_output_allow_play_all(void) | ||||||
| { | { | ||||||
| 	for (unsigned i = 0; i < num_audio_outputs; ++i) | 	for (unsigned i = 0; i < num_audio_outputs; ++i) | ||||||
| 		audio_output_allow_play(&audio_outputs[i]); | 		audio_output_allow_play(audio_outputs[i]); | ||||||
| } | } | ||||||
|  |  | ||||||
| static void | static void | ||||||
| @@ -238,7 +242,7 @@ static void | |||||||
| audio_output_all_reset_reopen(void) | audio_output_all_reset_reopen(void) | ||||||
| { | { | ||||||
| 	for (unsigned i = 0; i < num_audio_outputs; ++i) { | 	for (unsigned i = 0; i < num_audio_outputs; ++i) { | ||||||
| 		struct audio_output *ao = &audio_outputs[i]; | 		struct audio_output *ao = audio_outputs[i]; | ||||||
|  |  | ||||||
| 		audio_output_reset_reopen(ao); | 		audio_output_reset_reopen(ao); | ||||||
| 	} | 	} | ||||||
| @@ -259,7 +263,7 @@ audio_output_all_update(void) | |||||||
| 		return false; | 		return false; | ||||||
|  |  | ||||||
| 	for (i = 0; i < num_audio_outputs; ++i) | 	for (i = 0; i < num_audio_outputs; ++i) | ||||||
| 		ret = audio_output_update(&audio_outputs[i], | 		ret = audio_output_update(audio_outputs[i], | ||||||
| 					  &input_audio_format, g_mp) || ret; | 					  &input_audio_format, g_mp) || ret; | ||||||
|  |  | ||||||
| 	return ret; | 	return ret; | ||||||
| @@ -283,7 +287,7 @@ audio_output_all_play(struct music_chunk *chunk) | |||||||
| 	music_pipe_push(g_mp, chunk); | 	music_pipe_push(g_mp, chunk); | ||||||
|  |  | ||||||
| 	for (i = 0; i < num_audio_outputs; ++i) | 	for (i = 0; i < num_audio_outputs; ++i) | ||||||
| 		audio_output_play(&audio_outputs[i]); | 		audio_output_play(audio_outputs[i]); | ||||||
|  |  | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
| @@ -322,10 +326,10 @@ audio_output_all_open(const struct audio_format *audio_format, | |||||||
| 	audio_output_all_update(); | 	audio_output_all_update(); | ||||||
|  |  | ||||||
| 	for (i = 0; i < num_audio_outputs; ++i) { | 	for (i = 0; i < num_audio_outputs; ++i) { | ||||||
| 		if (audio_outputs[i].enabled) | 		if (audio_outputs[i]->enabled) | ||||||
| 			enabled = true; | 			enabled = true; | ||||||
|  |  | ||||||
| 		if (audio_outputs[i].open) | 		if (audio_outputs[i]->open) | ||||||
| 			ret = true; | 			ret = true; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -369,7 +373,7 @@ static bool | |||||||
| chunk_is_consumed(const struct music_chunk *chunk) | chunk_is_consumed(const struct music_chunk *chunk) | ||||||
| { | { | ||||||
| 	for (unsigned i = 0; i < num_audio_outputs; ++i) { | 	for (unsigned i = 0; i < num_audio_outputs; ++i) { | ||||||
| 		const struct audio_output *ao = &audio_outputs[i]; | 		const struct audio_output *ao = audio_outputs[i]; | ||||||
| 		bool consumed; | 		bool consumed; | ||||||
|  |  | ||||||
| 		g_mutex_lock(ao->mutex); | 		g_mutex_lock(ao->mutex); | ||||||
| @@ -394,7 +398,7 @@ clear_tail_chunk(G_GNUC_UNUSED const struct music_chunk *chunk, bool *locked) | |||||||
| 	assert(music_pipe_contains(g_mp, chunk)); | 	assert(music_pipe_contains(g_mp, chunk)); | ||||||
|  |  | ||||||
| 	for (unsigned i = 0; i < num_audio_outputs; ++i) { | 	for (unsigned i = 0; i < num_audio_outputs; ++i) { | ||||||
| 		struct audio_output *ao = &audio_outputs[i]; | 		struct audio_output *ao = audio_outputs[i]; | ||||||
|  |  | ||||||
| 		/* this mutex will be unlocked by the caller when it's | 		/* this mutex will be unlocked by the caller when it's | ||||||
| 		   ready */ | 		   ready */ | ||||||
| @@ -451,7 +455,7 @@ audio_output_all_check(void) | |||||||
| 			   by clear_tail_chunk() */ | 			   by clear_tail_chunk() */ | ||||||
| 			for (unsigned i = 0; i < num_audio_outputs; ++i) | 			for (unsigned i = 0; i < num_audio_outputs; ++i) | ||||||
| 				if (locked[i]) | 				if (locked[i]) | ||||||
| 					g_mutex_unlock(audio_outputs[i].mutex); | 					g_mutex_unlock(audio_outputs[i]->mutex); | ||||||
|  |  | ||||||
| 		/* return the chunk to the buffer */ | 		/* return the chunk to the buffer */ | ||||||
| 		music_buffer_return(g_music_buffer, shifted); | 		music_buffer_return(g_music_buffer, shifted); | ||||||
| @@ -484,7 +488,7 @@ audio_output_all_pause(void) | |||||||
| 	audio_output_all_update(); | 	audio_output_all_update(); | ||||||
|  |  | ||||||
| 	for (i = 0; i < num_audio_outputs; ++i) | 	for (i = 0; i < num_audio_outputs; ++i) | ||||||
| 		audio_output_pause(&audio_outputs[i]); | 		audio_output_pause(audio_outputs[i]); | ||||||
|  |  | ||||||
| 	audio_output_wait_all(); | 	audio_output_wait_all(); | ||||||
| } | } | ||||||
| @@ -493,7 +497,7 @@ void | |||||||
| audio_output_all_drain(void) | audio_output_all_drain(void) | ||||||
| { | { | ||||||
| 	for (unsigned i = 0; i < num_audio_outputs; ++i) | 	for (unsigned i = 0; i < num_audio_outputs; ++i) | ||||||
| 		audio_output_drain_async(&audio_outputs[i]); | 		audio_output_drain_async(audio_outputs[i]); | ||||||
|  |  | ||||||
| 	audio_output_wait_all(); | 	audio_output_wait_all(); | ||||||
| } | } | ||||||
| @@ -506,7 +510,7 @@ audio_output_all_cancel(void) | |||||||
| 	/* send the cancel() command to all audio outputs */ | 	/* send the cancel() command to all audio outputs */ | ||||||
|  |  | ||||||
| 	for (i = 0; i < num_audio_outputs; ++i) | 	for (i = 0; i < num_audio_outputs; ++i) | ||||||
| 		audio_output_cancel(&audio_outputs[i]); | 		audio_output_cancel(audio_outputs[i]); | ||||||
|  |  | ||||||
| 	audio_output_wait_all(); | 	audio_output_wait_all(); | ||||||
|  |  | ||||||
| @@ -531,7 +535,7 @@ audio_output_all_close(void) | |||||||
| 	unsigned int i; | 	unsigned int i; | ||||||
|  |  | ||||||
| 	for (i = 0; i < num_audio_outputs; ++i) | 	for (i = 0; i < num_audio_outputs; ++i) | ||||||
| 		audio_output_close(&audio_outputs[i]); | 		audio_output_close(audio_outputs[i]); | ||||||
|  |  | ||||||
| 	if (g_mp != NULL) { | 	if (g_mp != NULL) { | ||||||
| 		assert(g_music_buffer != NULL); | 		assert(g_music_buffer != NULL); | ||||||
| @@ -554,7 +558,7 @@ audio_output_all_release(void) | |||||||
| 	unsigned int i; | 	unsigned int i; | ||||||
|  |  | ||||||
| 	for (i = 0; i < num_audio_outputs; ++i) | 	for (i = 0; i < num_audio_outputs; ++i) | ||||||
| 		audio_output_release(&audio_outputs[i]); | 		audio_output_release(audio_outputs[i]); | ||||||
|  |  | ||||||
| 	if (g_mp != NULL) { | 	if (g_mp != NULL) { | ||||||
| 		assert(g_music_buffer != NULL); | 		assert(g_music_buffer != NULL); | ||||||
|   | |||||||
| @@ -21,6 +21,7 @@ | |||||||
| #define MPD_OUTPUT_API_H | #define MPD_OUTPUT_API_H | ||||||
|  |  | ||||||
| #include "output_plugin.h" | #include "output_plugin.h" | ||||||
|  | #include "output_internal.h" | ||||||
| #include "audio_format.h" | #include "audio_format.h" | ||||||
| #include "tag.h" | #include "tag.h" | ||||||
| #include "conf.h" | #include "conf.h" | ||||||
|   | |||||||
| @@ -335,5 +335,5 @@ void audio_output_finish(struct audio_output *ao) | |||||||
| 		ao->thread = NULL; | 		ao->thread = NULL; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	audio_output_destruct(ao); | 	audio_output_free(ao); | ||||||
| } | } | ||||||
|   | |||||||
| @@ -37,11 +37,6 @@ audio_output_quark(void) | |||||||
| 	return g_quark_from_static_string("audio_output"); | 	return g_quark_from_static_string("audio_output"); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool |  | ||||||
| audio_output_init(struct audio_output *ao, const struct config_param *param, |  | ||||||
| 		  struct player_control *pc, |  | ||||||
| 		  GError **error_r); |  | ||||||
|  |  | ||||||
| /** | /** | ||||||
|  * Enables the device. |  * Enables the device. | ||||||
|  */ |  */ | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ | |||||||
| #include <assert.h> | #include <assert.h> | ||||||
|  |  | ||||||
| void | void | ||||||
| audio_output_destruct(struct audio_output *ao) | ao_base_finish(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	assert(!ao->open); | 	assert(!ao->open); | ||||||
| 	assert(ao->fail_timer == NULL); | 	assert(ao->fail_timer == NULL); | ||||||
| @@ -35,8 +35,6 @@ audio_output_destruct(struct audio_output *ao) | |||||||
| 	if (ao->mixer != NULL) | 	if (ao->mixer != NULL) | ||||||
| 		mixer_free(ao->mixer); | 		mixer_free(ao->mixer); | ||||||
|  |  | ||||||
| 	ao_plugin_finish(ao->plugin, ao->data); |  | ||||||
|  |  | ||||||
| 	g_cond_free(ao->cond); | 	g_cond_free(ao->cond); | ||||||
| 	g_mutex_free(ao->mutex); | 	g_mutex_free(ao->mutex); | ||||||
|  |  | ||||||
| @@ -50,3 +48,13 @@ audio_output_destruct(struct audio_output *ao) | |||||||
|  |  | ||||||
| 	pcm_buffer_deinit(&ao->cross_fade_buffer); | 	pcm_buffer_deinit(&ao->cross_fade_buffer); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | audio_output_free(struct audio_output *ao) | ||||||
|  | { | ||||||
|  | 	assert(!ao->open); | ||||||
|  | 	assert(ao->fail_timer == NULL); | ||||||
|  | 	assert(ao->thread == NULL); | ||||||
|  |  | ||||||
|  | 	ao_plugin_finish(ao); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -94,7 +94,8 @@ audio_output_mixer_type(const struct config_param *param) | |||||||
| } | } | ||||||
|  |  | ||||||
| static struct mixer * | static struct mixer * | ||||||
| audio_output_load_mixer(void *ao, const struct config_param *param, | audio_output_load_mixer(struct audio_output *ao, | ||||||
|  | 			const struct config_param *param, | ||||||
| 			const struct mixer_plugin *plugin, | 			const struct mixer_plugin *plugin, | ||||||
| 			struct filter *filter_chain, | 			struct filter *filter_chain, | ||||||
| 			GError **error_r) | 			GError **error_r) | ||||||
| @@ -126,33 +127,22 @@ audio_output_load_mixer(void *ao, const struct config_param *param, | |||||||
| } | } | ||||||
|  |  | ||||||
| bool | bool | ||||||
| audio_output_init(struct audio_output *ao, const struct config_param *param, | ao_base_init(struct audio_output *ao, | ||||||
| 		  struct player_control *pc, | 	     const struct audio_output_plugin *plugin, | ||||||
| 		  GError **error_r) | 	     const struct config_param *param, GError **error_r) | ||||||
| { | { | ||||||
| 	assert(ao != NULL); | 	assert(ao != NULL); | ||||||
| 	assert(pc != NULL); | 	assert(plugin != NULL); | ||||||
|  | 	assert(plugin->finish != NULL); | ||||||
|  | 	assert(plugin->open != NULL); | ||||||
|  | 	assert(plugin->close != NULL); | ||||||
|  | 	assert(plugin->play != NULL); | ||||||
|  |  | ||||||
| 	const struct audio_output_plugin *plugin = NULL; |  | ||||||
| 	GError *error = NULL; | 	GError *error = NULL; | ||||||
|  |  | ||||||
| 	if (param) { | 	if (param) { | ||||||
| 		const char *p; | 		const char *p; | ||||||
|  |  | ||||||
| 		p = config_get_block_string(param, AUDIO_OUTPUT_TYPE, NULL); |  | ||||||
| 		if (p == NULL) { |  | ||||||
| 			g_set_error(error_r, audio_output_quark(), 0, |  | ||||||
| 				    "Missing \"type\" configuration"); |  | ||||||
| 			return false; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		plugin = audio_output_plugin_get(p); |  | ||||||
| 		if (plugin == NULL) { |  | ||||||
| 			g_set_error(error_r, audio_output_quark(), 0, |  | ||||||
| 				    "No such audio output plugin: %s", p); |  | ||||||
| 			return false; |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		ao->name = config_get_block_string(param, AUDIO_OUTPUT_NAME, | 		ao->name = config_get_block_string(param, AUDIO_OUTPUT_NAME, | ||||||
| 						   NULL); | 						   NULL); | ||||||
| 		if (ao->name == NULL) { | 		if (ao->name == NULL) { | ||||||
| @@ -172,16 +162,6 @@ audio_output_init(struct audio_output *ao, const struct config_param *param, | |||||||
| 		} else | 		} else | ||||||
| 			audio_format_clear(&ao->config_audio_format); | 			audio_format_clear(&ao->config_audio_format); | ||||||
| 	} else { | 	} else { | ||||||
| 		g_warning("No \"%s\" defined in config file\n", |  | ||||||
| 			  CONF_AUDIO_OUTPUT); |  | ||||||
|  |  | ||||||
| 		plugin = audio_output_detect(error_r); |  | ||||||
| 		if (plugin == NULL) |  | ||||||
| 			return false; |  | ||||||
|  |  | ||||||
| 		g_message("Successfully detected a %s audio device", |  | ||||||
| 			  plugin->name); |  | ||||||
|  |  | ||||||
| 		ao->name = "default detected output"; | 		ao->name = "default detected output"; | ||||||
|  |  | ||||||
| 		audio_format_clear(&ao->config_audio_format); | 		audio_format_clear(&ao->config_audio_format); | ||||||
| @@ -203,29 +183,6 @@ audio_output_init(struct audio_output *ao, const struct config_param *param, | |||||||
| 	ao->filter = filter_chain_new(); | 	ao->filter = filter_chain_new(); | ||||||
| 	assert(ao->filter != NULL); | 	assert(ao->filter != NULL); | ||||||
|  |  | ||||||
| 	/* create the replay_gain filter */ |  | ||||||
|  |  | ||||||
| 	const char *replay_gain_handler = |  | ||||||
| 		config_get_block_string(param, "replay_gain_handler", |  | ||||||
| 					"software"); |  | ||||||
|  |  | ||||||
| 	if (strcmp(replay_gain_handler, "none") != 0) { |  | ||||||
| 		ao->replay_gain_filter = filter_new(&replay_gain_filter_plugin, |  | ||||||
| 						    param, NULL); |  | ||||||
| 		assert(ao->replay_gain_filter != NULL); |  | ||||||
|  |  | ||||||
| 		ao->replay_gain_serial = 0; |  | ||||||
|  |  | ||||||
| 		ao->other_replay_gain_filter = filter_new(&replay_gain_filter_plugin, |  | ||||||
| 							  param, NULL); |  | ||||||
| 		assert(ao->other_replay_gain_filter != NULL); |  | ||||||
|  |  | ||||||
| 		ao->other_replay_gain_serial = 0; |  | ||||||
| 	} else { |  | ||||||
| 		ao->replay_gain_filter = NULL; |  | ||||||
| 		ao->other_replay_gain_filter = NULL; |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	/* create the normalization filter (if configured) */ | 	/* create the normalization filter (if configured) */ | ||||||
|  |  | ||||||
| 	if (config_get_bool(CONF_VOLUME_NORMALIZATION, false)) { | 	if (config_get_bool(CONF_VOLUME_NORMALIZATION, false)) { | ||||||
| @@ -254,16 +211,54 @@ audio_output_init(struct audio_output *ao, const struct config_param *param, | |||||||
| 	ao->command = AO_COMMAND_NONE; | 	ao->command = AO_COMMAND_NONE; | ||||||
| 	ao->mutex = g_mutex_new(); | 	ao->mutex = g_mutex_new(); | ||||||
| 	ao->cond = g_cond_new(); | 	ao->cond = g_cond_new(); | ||||||
| 	ao->player_control = pc; |  | ||||||
|  |  | ||||||
| 	ao->data = ao_plugin_init(plugin, | 	ao->mixer = NULL; | ||||||
| 				  &ao->config_audio_format, |  | ||||||
| 				  param, error_r); |  | ||||||
| 	if (ao->data == NULL) |  | ||||||
| 		return false; |  | ||||||
|  |  | ||||||
| 	ao->mixer = audio_output_load_mixer(ao->data, param, | 	/* the "convert" filter must be the last one in the chain */ | ||||||
| 					    plugin->mixer_plugin, |  | ||||||
|  | 	ao->convert_filter = filter_new(&convert_filter_plugin, NULL, NULL); | ||||||
|  | 	assert(ao->convert_filter != NULL); | ||||||
|  |  | ||||||
|  | 	filter_chain_append(ao->filter, ao->convert_filter); | ||||||
|  |  | ||||||
|  | 	/* done */ | ||||||
|  |  | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static bool | ||||||
|  | audio_output_setup(struct audio_output *ao, const struct config_param *param, | ||||||
|  | 		   GError **error_r) | ||||||
|  | { | ||||||
|  |  | ||||||
|  | 	/* create the replay_gain filter */ | ||||||
|  |  | ||||||
|  | 	const char *replay_gain_handler = | ||||||
|  | 		config_get_block_string(param, "replay_gain_handler", | ||||||
|  | 					"software"); | ||||||
|  |  | ||||||
|  | 	if (strcmp(replay_gain_handler, "none") != 0) { | ||||||
|  | 		ao->replay_gain_filter = filter_new(&replay_gain_filter_plugin, | ||||||
|  | 						    param, NULL); | ||||||
|  | 		assert(ao->replay_gain_filter != NULL); | ||||||
|  |  | ||||||
|  | 		ao->replay_gain_serial = 0; | ||||||
|  |  | ||||||
|  | 		ao->other_replay_gain_filter = filter_new(&replay_gain_filter_plugin, | ||||||
|  | 							  param, NULL); | ||||||
|  | 		assert(ao->other_replay_gain_filter != NULL); | ||||||
|  |  | ||||||
|  | 		ao->other_replay_gain_serial = 0; | ||||||
|  | 	} else { | ||||||
|  | 		ao->replay_gain_filter = NULL; | ||||||
|  | 		ao->other_replay_gain_filter = NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* set up the mixer */ | ||||||
|  |  | ||||||
|  | 	GError *error = NULL; | ||||||
|  | 	ao->mixer = audio_output_load_mixer(ao, param, | ||||||
|  | 					    ao->plugin->mixer_plugin, | ||||||
| 					    ao->filter, &error); | 					    ao->filter, &error); | ||||||
| 	if (ao->mixer == NULL && error != NULL) { | 	if (ao->mixer == NULL && error != NULL) { | ||||||
| 		g_warning("Failed to initialize hardware mixer for '%s': %s", | 		g_warning("Failed to initialize hardware mixer for '%s': %s", | ||||||
| @@ -286,14 +281,53 @@ audio_output_init(struct audio_output *ao, const struct config_param *param, | |||||||
| 		return false; | 		return false; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	/* the "convert" filter must be the last one in the chain */ |  | ||||||
|  |  | ||||||
| 	ao->convert_filter = filter_new(&convert_filter_plugin, NULL, NULL); |  | ||||||
| 	assert(ao->convert_filter != NULL); |  | ||||||
|  |  | ||||||
| 	filter_chain_append(ao->filter, ao->convert_filter); |  | ||||||
|  |  | ||||||
| 	/* done */ |  | ||||||
|  |  | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | struct audio_output * | ||||||
|  | audio_output_new(const struct config_param *param, | ||||||
|  | 		 struct player_control *pc, | ||||||
|  | 		 GError **error_r) | ||||||
|  | { | ||||||
|  | 	const struct audio_output_plugin *plugin; | ||||||
|  |  | ||||||
|  | 	if (param) { | ||||||
|  | 		const char *p; | ||||||
|  |  | ||||||
|  | 		p = config_get_block_string(param, AUDIO_OUTPUT_TYPE, NULL); | ||||||
|  | 		if (p == NULL) { | ||||||
|  | 			g_set_error(error_r, audio_output_quark(), 0, | ||||||
|  | 				    "Missing \"type\" configuration"); | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		plugin = audio_output_plugin_get(p); | ||||||
|  | 		if (plugin == NULL) { | ||||||
|  | 			g_set_error(error_r, audio_output_quark(), 0, | ||||||
|  | 				    "No such audio output plugin: %s", p); | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		g_warning("No \"%s\" defined in config file\n", | ||||||
|  | 			  CONF_AUDIO_OUTPUT); | ||||||
|  |  | ||||||
|  | 		plugin = audio_output_detect(error_r); | ||||||
|  | 		if (plugin == NULL) | ||||||
|  | 			return false; | ||||||
|  |  | ||||||
|  | 		g_message("Successfully detected a %s audio device", | ||||||
|  | 			  plugin->name); | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	struct audio_output *ao = ao_plugin_init(plugin, param, error_r); | ||||||
|  | 	if (ao == NULL) | ||||||
|  | 		return NULL; | ||||||
|  |  | ||||||
|  | 	if (!audio_output_setup(ao, param, error_r)) { | ||||||
|  | 		ao_plugin_finish(ao); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ao->player_control = pc; | ||||||
|  | 	return ao; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -27,6 +27,8 @@ | |||||||
|  |  | ||||||
| #include <time.h> | #include <time.h> | ||||||
|  |  | ||||||
|  | struct config_param; | ||||||
|  |  | ||||||
| enum audio_output_command { | enum audio_output_command { | ||||||
| 	AO_COMMAND_NONE = 0, | 	AO_COMMAND_NONE = 0, | ||||||
| 	AO_COMMAND_ENABLE, | 	AO_COMMAND_ENABLE, | ||||||
| @@ -63,12 +65,6 @@ struct audio_output { | |||||||
| 	 */ | 	 */ | ||||||
| 	const struct audio_output_plugin *plugin; | 	const struct audio_output_plugin *plugin; | ||||||
|  |  | ||||||
| 	/** |  | ||||||
| 	 * The plugin's internal data.  It is passed to every plugin |  | ||||||
| 	 * method. |  | ||||||
| 	 */ |  | ||||||
| 	void *data; |  | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * The #mixer object associated with this audio output device. | 	 * The #mixer object associated with this audio output device. | ||||||
| 	 * May be NULL if none is available, or if software volume is | 	 * May be NULL if none is available, or if software volume is | ||||||
| @@ -254,7 +250,20 @@ audio_output_command_is_finished(const struct audio_output *ao) | |||||||
| 	return ao->command == AO_COMMAND_NONE; | 	return ao->command == AO_COMMAND_NONE; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | struct audio_output * | ||||||
|  | audio_output_new(const struct config_param *param, | ||||||
|  | 		 struct player_control *pc, | ||||||
|  | 		 GError **error_r); | ||||||
|  |  | ||||||
|  | bool | ||||||
|  | ao_base_init(struct audio_output *ao, | ||||||
|  | 	     const struct audio_output_plugin *plugin, | ||||||
|  | 	     const struct config_param *param, GError **error_r); | ||||||
|  |  | ||||||
| void | void | ||||||
| audio_output_destruct(struct audio_output *ao); | ao_base_finish(struct audio_output *ao); | ||||||
|  |  | ||||||
|  | void | ||||||
|  | audio_output_free(struct audio_output *ao); | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
							
								
								
									
										108
									
								
								src/output_plugin.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/output_plugin.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | |||||||
|  | /* | ||||||
|  |  * Copyright (C) 2003-2011 The Music Player Daemon Project | ||||||
|  |  * http://www.musicpd.org | ||||||
|  |  * | ||||||
|  |  * This program is free software; you can redistribute it and/or modify | ||||||
|  |  * it under the terms of the GNU General Public License as published by | ||||||
|  |  * the Free Software Foundation; either version 2 of the License, or | ||||||
|  |  * (at your option) any later version. | ||||||
|  |  * | ||||||
|  |  * This program is distributed in the hope that it will be useful, | ||||||
|  |  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |  * GNU General Public License for more details. | ||||||
|  |  * | ||||||
|  |  * You should have received a copy of the GNU General Public License along | ||||||
|  |  * with this program; if not, write to the Free Software Foundation, Inc., | ||||||
|  |  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "output_plugin.h" | ||||||
|  | #include "output_internal.h" | ||||||
|  |  | ||||||
|  | struct audio_output * | ||||||
|  | ao_plugin_init(const struct audio_output_plugin *plugin, | ||||||
|  | 	       const struct config_param *param, | ||||||
|  | 	       GError **error) | ||||||
|  | { | ||||||
|  | 	assert(plugin != NULL); | ||||||
|  | 	assert(plugin->init != NULL); | ||||||
|  |  | ||||||
|  | 	return plugin->init(param, error); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | ao_plugin_finish(struct audio_output *ao) | ||||||
|  | { | ||||||
|  | 	ao->plugin->finish(ao); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool | ||||||
|  | ao_plugin_enable(struct audio_output *ao, GError **error_r) | ||||||
|  | { | ||||||
|  | 	return ao->plugin->enable != NULL | ||||||
|  | 		? ao->plugin->enable(ao, error_r) | ||||||
|  | 		: true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | ao_plugin_disable(struct audio_output *ao) | ||||||
|  | { | ||||||
|  | 	if (ao->plugin->disable != NULL) | ||||||
|  | 		ao->plugin->disable(ao); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool | ||||||
|  | ao_plugin_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
|  | 	       GError **error) | ||||||
|  | { | ||||||
|  | 	return ao->plugin->open(ao, audio_format, error); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | ao_plugin_close(struct audio_output *ao) | ||||||
|  | { | ||||||
|  | 	ao->plugin->close(ao); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | unsigned | ||||||
|  | ao_plugin_delay(struct audio_output *ao) | ||||||
|  | { | ||||||
|  | 	return ao->plugin->delay != NULL | ||||||
|  | 		? ao->plugin->delay(ao) | ||||||
|  | 		: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | ao_plugin_send_tag(struct audio_output *ao, const struct tag *tag) | ||||||
|  | { | ||||||
|  | 	if (ao->plugin->send_tag != NULL) | ||||||
|  | 		ao->plugin->send_tag(ao, tag); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | size_t | ||||||
|  | ao_plugin_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
|  | 	       GError **error) | ||||||
|  | { | ||||||
|  | 	return ao->plugin->play(ao, chunk, size, error); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | ao_plugin_drain(struct audio_output *ao) | ||||||
|  | { | ||||||
|  | 	if (ao->plugin->drain != NULL) | ||||||
|  | 		ao->plugin->drain(ao); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void | ||||||
|  | ao_plugin_cancel(struct audio_output *ao) | ||||||
|  | { | ||||||
|  | 	if (ao->plugin->cancel != NULL) | ||||||
|  | 		ao->plugin->cancel(ao); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool | ||||||
|  | ao_plugin_pause(struct audio_output *ao) | ||||||
|  | { | ||||||
|  | 	return ao->plugin->pause != NULL && ao->plugin->pause(ao); | ||||||
|  | } | ||||||
| @@ -48,8 +48,6 @@ struct audio_output_plugin { | |||||||
| 	 * Configure and initialize the device, but do not open it | 	 * Configure and initialize the device, but do not open it | ||||||
| 	 * yet. | 	 * yet. | ||||||
| 	 * | 	 * | ||||||
| 	 * @param audio_format the configured audio format, or NULL if |  | ||||||
| 	 * none is configured |  | ||||||
| 	 * @param param the configuration section, or NULL if there is | 	 * @param param the configuration section, or NULL if there is | ||||||
| 	 * no configuration | 	 * no configuration | ||||||
| 	 * @param error location to store the error occurring, or NULL | 	 * @param error location to store the error occurring, or NULL | ||||||
| @@ -57,14 +55,13 @@ struct audio_output_plugin { | |||||||
| 	 * @return NULL on error, or an opaque pointer to the plugin's | 	 * @return NULL on error, or an opaque pointer to the plugin's | ||||||
| 	 * data | 	 * data | ||||||
| 	 */ | 	 */ | ||||||
| 	void *(*init)(const struct audio_format *audio_format, | 	struct audio_output *(*init)(const struct config_param *param, | ||||||
| 		      const struct config_param *param, |  | ||||||
| 				     GError **error); | 				     GError **error); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Free resources allocated by this device. | 	 * Free resources allocated by this device. | ||||||
| 	 */ | 	 */ | ||||||
| 	void (*finish)(void *data); | 	void (*finish)(struct audio_output *data); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Enable the device.  This may allocate resources, preparing | 	 * Enable the device.  This may allocate resources, preparing | ||||||
| @@ -76,13 +73,13 @@ struct audio_output_plugin { | |||||||
| 	 * NULL to ignore errors | 	 * NULL to ignore errors | ||||||
| 	 * @return true on success, false on error | 	 * @return true on success, false on error | ||||||
| 	 */ | 	 */ | ||||||
| 	bool (*enable)(void *data, GError **error_r); | 	bool (*enable)(struct audio_output *data, GError **error_r); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Disables the device.  It is closed before this method is | 	 * Disables the device.  It is closed before this method is | ||||||
| 	 * called. | 	 * called. | ||||||
| 	 */ | 	 */ | ||||||
| 	void (*disable)(void *data); | 	void (*disable)(struct audio_output *data); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Really open the device. | 	 * Really open the device. | ||||||
| @@ -92,13 +89,13 @@ struct audio_output_plugin { | |||||||
| 	 * @param error location to store the error occurring, or NULL | 	 * @param error location to store the error occurring, or NULL | ||||||
| 	 * to ignore errors | 	 * to ignore errors | ||||||
| 	 */ | 	 */ | ||||||
| 	bool (*open)(void *data, struct audio_format *audio_format, | 	bool (*open)(struct audio_output *data, struct audio_format *audio_format, | ||||||
| 		     GError **error); | 		     GError **error); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Close the device. | 	 * Close the device. | ||||||
| 	 */ | 	 */ | ||||||
| 	void (*close)(void *data); | 	void (*close)(struct audio_output *data); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Returns a positive number if the output thread shall delay | 	 * Returns a positive number if the output thread shall delay | ||||||
| @@ -108,13 +105,13 @@ struct audio_output_plugin { | |||||||
| 	 * | 	 * | ||||||
| 	 * @return the number of milliseconds to wait | 	 * @return the number of milliseconds to wait | ||||||
| 	 */ | 	 */ | ||||||
| 	unsigned (*delay)(void *data); | 	unsigned (*delay)(struct audio_output *data); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Display metadata for the next chunk.  Optional method, | 	 * Display metadata for the next chunk.  Optional method, | ||||||
| 	 * because not all devices can display metadata. | 	 * because not all devices can display metadata. | ||||||
| 	 */ | 	 */ | ||||||
| 	void (*send_tag)(void *data, const struct tag *tag); | 	void (*send_tag)(struct audio_output *data, const struct tag *tag); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Play a chunk of audio data. | 	 * Play a chunk of audio data. | ||||||
| @@ -123,19 +120,20 @@ struct audio_output_plugin { | |||||||
| 	 * to ignore errors | 	 * to ignore errors | ||||||
| 	 * @return the number of bytes played, or 0 on error | 	 * @return the number of bytes played, or 0 on error | ||||||
| 	 */ | 	 */ | ||||||
| 	size_t (*play)(void *data, const void *chunk, size_t size, | 	size_t (*play)(struct audio_output *data, | ||||||
|  | 		       const void *chunk, size_t size, | ||||||
| 		       GError **error); | 		       GError **error); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Wait until the device has finished playing. | 	 * Wait until the device has finished playing. | ||||||
| 	 */ | 	 */ | ||||||
| 	void (*drain)(void *data); | 	void (*drain)(struct audio_output *data); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Try to cancel data which may still be in the device's | 	 * Try to cancel data which may still be in the device's | ||||||
| 	 * buffers. | 	 * buffers. | ||||||
| 	 */ | 	 */ | ||||||
| 	void (*cancel)(void *data); | 	void (*cancel)(struct audio_output *data); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * Pause the device.  If supported, it may perform a special | 	 * Pause the device.  If supported, it may perform a special | ||||||
| @@ -148,7 +146,7 @@ struct audio_output_plugin { | |||||||
| 	 * @return false on error (output will be closed then), true | 	 * @return false on error (output will be closed then), true | ||||||
| 	 * for continue to pause | 	 * for continue to pause | ||||||
| 	 */ | 	 */ | ||||||
| 	bool (*pause)(void *data); | 	bool (*pause)(struct audio_output *data); | ||||||
|  |  | ||||||
| 	/** | 	/** | ||||||
| 	 * The mixer plugin associated with this output plugin.  This | 	 * The mixer plugin associated with this output plugin.  This | ||||||
| @@ -167,95 +165,46 @@ ao_plugin_test_default_device(const struct audio_output_plugin *plugin) | |||||||
| 		: false; | 		: false; | ||||||
| } | } | ||||||
|  |  | ||||||
| static inline void * | G_GNUC_MALLOC | ||||||
|  | struct audio_output * | ||||||
| ao_plugin_init(const struct audio_output_plugin *plugin, | ao_plugin_init(const struct audio_output_plugin *plugin, | ||||||
| 	       const struct audio_format *audio_format, |  | ||||||
| 	       const struct config_param *param, | 	       const struct config_param *param, | ||||||
| 	       GError **error) | 	       GError **error); | ||||||
| { |  | ||||||
| 	return plugin->init(audio_format, param, error); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void | void | ||||||
| ao_plugin_finish(const struct audio_output_plugin *plugin, void *data) | ao_plugin_finish(struct audio_output *ao); | ||||||
| { |  | ||||||
| 	plugin->finish(data); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline bool | bool | ||||||
| ao_plugin_enable(const struct audio_output_plugin *plugin, void *data, | ao_plugin_enable(struct audio_output *ao, GError **error_r); | ||||||
| 		 GError **error_r) |  | ||||||
| { |  | ||||||
| 	return plugin->enable != NULL |  | ||||||
| 		? plugin->enable(data, error_r) |  | ||||||
| 		: true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void | void | ||||||
| ao_plugin_disable(const struct audio_output_plugin *plugin, void *data) | ao_plugin_disable(struct audio_output *ao); | ||||||
| { |  | ||||||
| 	if (plugin->disable != NULL) |  | ||||||
| 		plugin->disable(data); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline bool | bool | ||||||
| ao_plugin_open(const struct audio_output_plugin *plugin, | ao_plugin_open(struct audio_output *ao, struct audio_format *audio_format, | ||||||
| 	       void *data, struct audio_format *audio_format, | 	       GError **error); | ||||||
| 	       GError **error) |  | ||||||
| { |  | ||||||
| 	return plugin->open(data, audio_format, error); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void | void | ||||||
| ao_plugin_close(const struct audio_output_plugin *plugin, void *data) | ao_plugin_close(struct audio_output *ao); | ||||||
| { |  | ||||||
| 	plugin->close(data); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline unsigned | G_GNUC_PURE | ||||||
| ao_plugin_delay(const struct audio_output_plugin *plugin, void *data) | unsigned | ||||||
| { | ao_plugin_delay(struct audio_output *ao); | ||||||
| 	return plugin->delay != NULL |  | ||||||
| 		? plugin->delay(data) |  | ||||||
| 		: 0; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void | void | ||||||
| ao_plugin_send_tag(const struct audio_output_plugin *plugin, | ao_plugin_send_tag(struct audio_output *ao, const struct tag *tag); | ||||||
| 		   void *data, const struct tag *tag) |  | ||||||
| { |  | ||||||
| 	if (plugin->send_tag != NULL) |  | ||||||
| 		plugin->send_tag(data, tag); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline size_t | size_t | ||||||
| ao_plugin_play(const struct audio_output_plugin *plugin, | ao_plugin_play(struct audio_output *ao, const void *chunk, size_t size, | ||||||
| 	       void *data, const void *chunk, size_t size, | 	       GError **error); | ||||||
| 	       GError **error) |  | ||||||
| { |  | ||||||
| 	return plugin->play(data, chunk, size, error); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void | void | ||||||
| ao_plugin_drain(const struct audio_output_plugin *plugin, void *data) | ao_plugin_drain(struct audio_output *ao); | ||||||
| { |  | ||||||
| 	if (plugin->drain != NULL) |  | ||||||
| 		plugin->drain(data); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline void | void | ||||||
| ao_plugin_cancel(const struct audio_output_plugin *plugin, void *data) | ao_plugin_cancel(struct audio_output *ao); | ||||||
| { |  | ||||||
| 	if (plugin->cancel != NULL) |  | ||||||
| 		plugin->cancel(data); |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static inline bool | bool | ||||||
| ao_plugin_pause(const struct audio_output_plugin *plugin, void *data) | ao_plugin_pause(struct audio_output *ao); | ||||||
| { |  | ||||||
| 	return plugin->pause != NULL |  | ||||||
| 		? plugin->pause(data) |  | ||||||
| 		: false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| #endif | #endif | ||||||
|   | |||||||
| @@ -60,7 +60,7 @@ ao_enable(struct audio_output *ao) | |||||||
| 		return true; | 		return true; | ||||||
|  |  | ||||||
| 	g_mutex_unlock(ao->mutex); | 	g_mutex_unlock(ao->mutex); | ||||||
| 	success = ao_plugin_enable(ao->plugin, ao->data, &error); | 	success = ao_plugin_enable(ao, &error); | ||||||
| 	g_mutex_lock(ao->mutex); | 	g_mutex_lock(ao->mutex); | ||||||
| 	if (!success) { | 	if (!success) { | ||||||
| 		g_warning("Failed to enable \"%s\" [%s]: %s\n", | 		g_warning("Failed to enable \"%s\" [%s]: %s\n", | ||||||
| @@ -86,7 +86,7 @@ ao_disable(struct audio_output *ao) | |||||||
| 		ao->really_enabled = false; | 		ao->really_enabled = false; | ||||||
|  |  | ||||||
| 		g_mutex_unlock(ao->mutex); | 		g_mutex_unlock(ao->mutex); | ||||||
| 		ao_plugin_disable(ao->plugin, ao->data); | 		ao_plugin_disable(ao); | ||||||
| 		g_mutex_lock(ao->mutex); | 		g_mutex_lock(ao->mutex); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @@ -175,9 +175,7 @@ ao_open(struct audio_output *ao) | |||||||
| 				&ao->config_audio_format); | 				&ao->config_audio_format); | ||||||
|  |  | ||||||
| 	g_mutex_unlock(ao->mutex); | 	g_mutex_unlock(ao->mutex); | ||||||
| 	success = ao_plugin_open(ao->plugin, ao->data, | 	success = ao_plugin_open(ao, &ao->out_audio_format, &error); | ||||||
| 				 &ao->out_audio_format, |  | ||||||
| 				 &error); |  | ||||||
| 	g_mutex_lock(ao->mutex); | 	g_mutex_lock(ao->mutex); | ||||||
|  |  | ||||||
| 	assert(!ao->open); | 	assert(!ao->open); | ||||||
| @@ -221,11 +219,11 @@ ao_close(struct audio_output *ao, bool drain) | |||||||
| 	g_mutex_unlock(ao->mutex); | 	g_mutex_unlock(ao->mutex); | ||||||
|  |  | ||||||
| 	if (drain) | 	if (drain) | ||||||
| 		ao_plugin_drain(ao->plugin, ao->data); | 		ao_plugin_drain(ao); | ||||||
| 	else | 	else | ||||||
| 		ao_plugin_cancel(ao->plugin, ao->data); | 		ao_plugin_cancel(ao); | ||||||
|  |  | ||||||
| 	ao_plugin_close(ao->plugin, ao->data); | 	ao_plugin_close(ao); | ||||||
| 	ao_filter_close(ao); | 	ao_filter_close(ao); | ||||||
|  |  | ||||||
| 	g_mutex_lock(ao->mutex); | 	g_mutex_lock(ao->mutex); | ||||||
| @@ -257,7 +255,7 @@ ao_reopen_filter(struct audio_output *ao) | |||||||
| 		ao->fail_timer = g_timer_new(); | 		ao->fail_timer = g_timer_new(); | ||||||
|  |  | ||||||
| 		g_mutex_unlock(ao->mutex); | 		g_mutex_unlock(ao->mutex); | ||||||
| 		ao_plugin_close(ao->plugin, ao->data); | 		ao_plugin_close(ao); | ||||||
| 		g_mutex_lock(ao->mutex); | 		g_mutex_lock(ao->mutex); | ||||||
|  |  | ||||||
| 		return; | 		return; | ||||||
| @@ -302,7 +300,7 @@ static bool | |||||||
| ao_wait(struct audio_output *ao) | ao_wait(struct audio_output *ao) | ||||||
| { | { | ||||||
| 	while (true) { | 	while (true) { | ||||||
| 		unsigned delay = ao_plugin_delay(ao->plugin, ao->data); | 		unsigned delay = ao_plugin_delay(ao); | ||||||
| 		if (delay == 0) | 		if (delay == 0) | ||||||
| 			return true; | 			return true; | ||||||
|  |  | ||||||
| @@ -434,7 +432,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) | |||||||
|  |  | ||||||
| 	if (chunk->tag != NULL) { | 	if (chunk->tag != NULL) { | ||||||
| 		g_mutex_unlock(ao->mutex); | 		g_mutex_unlock(ao->mutex); | ||||||
| 		ao_plugin_send_tag(ao->plugin, ao->data, chunk->tag); | 		ao_plugin_send_tag(ao, chunk->tag); | ||||||
| 		g_mutex_lock(ao->mutex); | 		g_mutex_lock(ao->mutex); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -456,8 +454,7 @@ ao_play_chunk(struct audio_output *ao, const struct music_chunk *chunk) | |||||||
| 			break; | 			break; | ||||||
|  |  | ||||||
| 		g_mutex_unlock(ao->mutex); | 		g_mutex_unlock(ao->mutex); | ||||||
| 		nbytes = ao_plugin_play(ao->plugin, ao->data, data, size, | 		nbytes = ao_plugin_play(ao, data, size, &error); | ||||||
| 					&error); |  | ||||||
| 		g_mutex_lock(ao->mutex); | 		g_mutex_lock(ao->mutex); | ||||||
| 		if (nbytes == 0) { | 		if (nbytes == 0) { | ||||||
| 			/* play()==0 means failure */ | 			/* play()==0 means failure */ | ||||||
| @@ -547,7 +544,7 @@ static void ao_pause(struct audio_output *ao) | |||||||
| 	bool ret; | 	bool ret; | ||||||
|  |  | ||||||
| 	g_mutex_unlock(ao->mutex); | 	g_mutex_unlock(ao->mutex); | ||||||
| 	ao_plugin_cancel(ao->plugin, ao->data); | 	ao_plugin_cancel(ao); | ||||||
| 	g_mutex_lock(ao->mutex); | 	g_mutex_lock(ao->mutex); | ||||||
|  |  | ||||||
| 	ao->pause = true; | 	ao->pause = true; | ||||||
| @@ -558,7 +555,7 @@ static void ao_pause(struct audio_output *ao) | |||||||
| 			break; | 			break; | ||||||
|  |  | ||||||
| 		g_mutex_unlock(ao->mutex); | 		g_mutex_unlock(ao->mutex); | ||||||
| 		ret = ao_plugin_pause(ao->plugin, ao->data); | 		ret = ao_plugin_pause(ao); | ||||||
| 		g_mutex_lock(ao->mutex); | 		g_mutex_lock(ao->mutex); | ||||||
|  |  | ||||||
| 		if (!ret) { | 		if (!ret) { | ||||||
| @@ -632,7 +629,7 @@ static gpointer audio_output_task(gpointer arg) | |||||||
| 				assert(music_pipe_peek(ao->pipe) == NULL); | 				assert(music_pipe_peek(ao->pipe) == NULL); | ||||||
|  |  | ||||||
| 				g_mutex_unlock(ao->mutex); | 				g_mutex_unlock(ao->mutex); | ||||||
| 				ao_plugin_drain(ao->plugin, ao->data); | 				ao_plugin_drain(ao); | ||||||
| 				g_mutex_lock(ao->mutex); | 				g_mutex_lock(ao->mutex); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
| @@ -644,7 +641,7 @@ static gpointer audio_output_task(gpointer arg) | |||||||
|  |  | ||||||
| 			if (ao->open) { | 			if (ao->open) { | ||||||
| 				g_mutex_unlock(ao->mutex); | 				g_mutex_unlock(ao->mutex); | ||||||
| 				ao_plugin_cancel(ao->plugin, ao->data); | 				ao_plugin_cancel(ao); | ||||||
| 				g_mutex_lock(ao->mutex); | 				g_mutex_lock(ao->mutex); | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -94,11 +94,10 @@ find_named_config_block(const char *block, const char *name) | |||||||
| 	return NULL; | 	return NULL; | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static struct audio_output * | ||||||
| load_audio_output(struct audio_output *ao, const char *name) | load_audio_output(const char *name) | ||||||
| { | { | ||||||
| 	const struct config_param *param; | 	const struct config_param *param; | ||||||
| 	bool success; |  | ||||||
| 	GError *error = NULL; | 	GError *error = NULL; | ||||||
|  |  | ||||||
| 	param = find_named_config_block(CONF_AUDIO_OUTPUT, name); | 	param = find_named_config_block(CONF_AUDIO_OUTPUT, name); | ||||||
| @@ -109,13 +108,14 @@ load_audio_output(struct audio_output *ao, const char *name) | |||||||
|  |  | ||||||
| 	static struct player_control dummy_player_control; | 	static struct player_control dummy_player_control; | ||||||
|  |  | ||||||
| 	success = audio_output_init(ao, param, &dummy_player_control, &error); | 	struct audio_output *ao = | ||||||
| 	if (!success) { | 		audio_output_new(param, &dummy_player_control, &error); | ||||||
|  | 	if (ao == NULL) { | ||||||
| 		g_printerr("%s\n", error->message); | 		g_printerr("%s\n", error->message); | ||||||
| 		g_error_free(error); | 		g_error_free(error); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return success; | 	return ao; | ||||||
| } | } | ||||||
|  |  | ||||||
| static bool | static bool | ||||||
| @@ -124,7 +124,7 @@ run_output(struct audio_output *ao, struct audio_format *audio_format) | |||||||
| 	/* open the audio output */ | 	/* open the audio output */ | ||||||
|  |  | ||||||
| 	GError *error = NULL; | 	GError *error = NULL; | ||||||
| 	if (!ao_plugin_open(ao->plugin, ao->data, audio_format, &error)) { | 	if (!ao_plugin_open(ao, audio_format, &error)) { | ||||||
| 		g_printerr("Failed to open audio output: %s\n", | 		g_printerr("Failed to open audio output: %s\n", | ||||||
| 			   error->message); | 			   error->message); | ||||||
| 		g_error_free(error); | 		g_error_free(error); | ||||||
| @@ -153,11 +153,11 @@ run_output(struct audio_output *ao, struct audio_format *audio_format) | |||||||
|  |  | ||||||
| 		size_t play_length = (length / frame_size) * frame_size; | 		size_t play_length = (length / frame_size) * frame_size; | ||||||
| 		if (play_length > 0) { | 		if (play_length > 0) { | ||||||
| 			size_t consumed = ao_plugin_play(ao->plugin, ao->data, | 			size_t consumed = ao_plugin_play(ao, | ||||||
| 							 buffer, play_length, | 							 buffer, play_length, | ||||||
| 							 &error); | 							 &error); | ||||||
| 			if (consumed == 0) { | 			if (consumed == 0) { | ||||||
| 				ao_plugin_close(ao->plugin, ao->data); | 				ao_plugin_close(ao); | ||||||
| 				g_printerr("Failed to play: %s\n", | 				g_printerr("Failed to play: %s\n", | ||||||
| 					   error->message); | 					   error->message); | ||||||
| 				g_error_free(error); | 				g_error_free(error); | ||||||
| @@ -172,13 +172,12 @@ run_output(struct audio_output *ao, struct audio_format *audio_format) | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	ao_plugin_close(ao->plugin, ao->data); | 	ao_plugin_close(ao); | ||||||
| 	return true; | 	return true; | ||||||
| } | } | ||||||
|  |  | ||||||
| int main(int argc, char **argv) | int main(int argc, char **argv) | ||||||
| { | { | ||||||
| 	struct audio_output ao; |  | ||||||
| 	struct audio_format audio_format; | 	struct audio_format audio_format; | ||||||
| 	bool success; | 	bool success; | ||||||
| 	GError *error = NULL; | 	GError *error = NULL; | ||||||
| @@ -211,7 +210,8 @@ int main(int argc, char **argv) | |||||||
|  |  | ||||||
| 	/* initialize the audio output */ | 	/* initialize the audio output */ | ||||||
|  |  | ||||||
| 	if (!load_audio_output(&ao, argv[2])) | 	struct audio_output *ao = load_audio_output(argv[2]); | ||||||
|  | 	if (ao == NULL) | ||||||
| 		return 1; | 		return 1; | ||||||
|  |  | ||||||
| 	/* parse the audio format */ | 	/* parse the audio format */ | ||||||
| @@ -229,11 +229,11 @@ int main(int argc, char **argv) | |||||||
|  |  | ||||||
| 	/* do it */ | 	/* do it */ | ||||||
|  |  | ||||||
| 	success = run_output(&ao, &audio_format); | 	success = run_output(ao, &audio_format); | ||||||
|  |  | ||||||
| 	/* cleanup and exit */ | 	/* cleanup and exit */ | ||||||
|  |  | ||||||
| 	audio_output_destruct(&ao); | 	audio_output_free(ao); | ||||||
|  |  | ||||||
| 	io_thread_deinit(); | 	io_thread_deinit(); | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Max Kellermann
					Max Kellermann