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:
parent
b4a8b8c0d4
commit
74617389c8
@ -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();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user