diff options
Diffstat (limited to '')
-rw-r--r-- | src/output/oss_output_plugin.c (renamed from src/output/oss_plugin.c) | 246 |
1 files changed, 175 insertions, 71 deletions
diff --git a/src/output/oss_plugin.c b/src/output/oss_output_plugin.c index 9261b423c..e366a4537 100644 --- a/src/output/oss_plugin.c +++ b/src/output/oss_output_plugin.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2003-2010 The Music Player Daemon Project + * 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 @@ -18,9 +18,11 @@ */ #include "config.h" +#include "oss_output_plugin.h" #include "output_api.h" #include "mixer_list.h" #include "fd_util.h" +#include "glib_compat.h" #include <glib.h> @@ -50,7 +52,17 @@ #undef AFMT_S24_NE #endif +#ifdef AFMT_S24_PACKED +#include "pcm_export.h" +#endif + struct oss_data { + struct audio_output base; + +#ifdef AFMT_S24_PACKED + struct pcm_export_state export; +#endif + int fd; const char *device; @@ -59,6 +71,12 @@ struct oss_data { * the device after cancel(). */ struct audio_format audio_format; + + /** + * The current OSS audio format. This is needed to reopen the + * device after cancel(). + */ + int oss_format; }; /** @@ -136,13 +154,13 @@ oss_output_test_default_device(void) return true; } g_warning("Error opening OSS device \"%s\": %s\n", - default_devices[i], strerror(errno)); + default_devices[i], g_strerror(errno)); } return false; } -static void * +static struct audio_output * oss_open_default(GError **error) { int i; @@ -153,8 +171,14 @@ oss_open_default(GError **error) ret[i] = oss_stat_device(default_devices[i], &err[i]); if (ret[i] == OSS_STAT_NO_ERROR) { 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]; - return od; + return &od->base; } } @@ -175,7 +199,7 @@ oss_open_default(GError **error) break; case OSS_STAT_OTHER: g_warning("Error accessing %s: %s\n", - dev, strerror(err[i])); + dev, g_strerror(err[i])); } } @@ -184,29 +208,55 @@ oss_open_default(GError **error) return NULL; } -static void * -oss_output_init(G_GNUC_UNUSED const struct audio_format *audio_format, - const struct config_param *param, - GError **error) +static struct audio_output * +oss_output_init(const struct config_param *param, GError **error) { const char *device = config_get_block_string(param, "device", NULL); if (device != NULL) { 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; - return od; + return &od->base; } return oss_open_default(error); } 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); } +#ifdef AFMT_S24_PACKED + +static bool +oss_output_enable(struct audio_output *ao, G_GNUC_UNUSED GError **error_r) +{ + struct oss_data *od = (struct oss_data *)ao; + + pcm_export_init(&od->export); + return true; +} + +static void +oss_output_disable(struct audio_output *ao) +{ + struct oss_data *od = (struct oss_data *)ao; + + pcm_export_deinit(&od->export); +} + +#endif + static void oss_close(struct oss_data *od) { @@ -381,6 +431,8 @@ sample_format_to_oss(enum sample_format format) { switch (format) { case SAMPLE_FORMAT_UNDEFINED: + case SAMPLE_FORMAT_FLOAT: + case SAMPLE_FORMAT_DSD: return AFMT_QUERY; case SAMPLE_FORMAT_S8: @@ -389,13 +441,6 @@ sample_format_to_oss(enum sample_format format) case SAMPLE_FORMAT_S16: return AFMT_S16_NE; - case SAMPLE_FORMAT_S24: -#ifdef AFMT_S24_PACKED - return AFMT_S24_PACKED; -#else - return AFMT_QUERY; -#endif - case SAMPLE_FORMAT_S24_P32: #ifdef AFMT_S24_NE return AFMT_S24_NE; @@ -430,7 +475,7 @@ sample_format_from_oss(int format) #ifdef AFMT_S24_PACKED case AFMT_S24_PACKED: - return SAMPLE_FORMAT_S24; + return SAMPLE_FORMAT_S24_P32; #endif #ifdef AFMT_S24_NE @@ -449,33 +494,83 @@ sample_format_from_oss(int format) } /** + * Probe one sample format. + * + * @return the selected sample format or SAMPLE_FORMAT_UNDEFINED on + * error + */ +static enum oss_setup_result +oss_probe_sample_format(int fd, enum sample_format sample_format, + enum sample_format *sample_format_r, + int *oss_format_r, +#ifdef AFMT_S24_PACKED + struct pcm_export_state *export, +#endif + GError **error_r) +{ + int oss_format = sample_format_to_oss(sample_format); + if (oss_format == AFMT_QUERY) + return UNSUPPORTED; + + enum oss_setup_result result = + oss_try_ioctl_r(fd, SNDCTL_DSP_SAMPLESIZE, + &oss_format, + "Failed to set sample format", error_r); + +#ifdef AFMT_S24_PACKED + if (result == UNSUPPORTED && sample_format == SAMPLE_FORMAT_S24_P32) { + /* if the driver doesn't support padded 24 bit, try + packed 24 bit */ + oss_format = AFMT_S24_PACKED; + result = oss_try_ioctl_r(fd, SNDCTL_DSP_SAMPLESIZE, + &oss_format, + "Failed to set sample format", error_r); + } +#endif + + if (result != SUCCESS) + return result; + + sample_format = sample_format_from_oss(oss_format); + if (sample_format == SAMPLE_FORMAT_UNDEFINED) + return UNSUPPORTED; + + *sample_format_r = sample_format; + *oss_format_r = oss_format; + +#ifdef AFMT_S24_PACKED + pcm_export_open(export, sample_format, 0, false, false, + oss_format == AFMT_S24_PACKED, + oss_format == AFMT_S24_PACKED && + G_BYTE_ORDER != G_LITTLE_ENDIAN); +#endif + + return SUCCESS; +} + +/** * Set up the sample format, and attempts to find alternatives if the * specified format is not supported. */ static bool oss_setup_sample_format(int fd, struct audio_format *audio_format, + int *oss_format_r, +#ifdef AFMT_S24_PACKED + struct pcm_export_state *export, +#endif GError **error_r) { - const char *const msg = "Failed to set sample format"; - int oss_format = sample_format_to_oss(audio_format->format); - enum oss_setup_result result = oss_format != AFMT_QUERY - ? oss_try_ioctl_r(fd, SNDCTL_DSP_SAMPLESIZE, - &oss_format, msg, error_r) - : UNSUPPORTED; enum sample_format mpd_format; + enum oss_setup_result result = + oss_probe_sample_format(fd, audio_format->format, + &mpd_format, oss_format_r, +#ifdef AFMT_S24_PACKED + export, +#endif + error_r); switch (result) { case SUCCESS: - mpd_format = sample_format_from_oss(oss_format); - if (mpd_format == SAMPLE_FORMAT_UNDEFINED) - break; - audio_format->format = mpd_format; - -#ifdef AFMT_S24_PACKED - if (oss_format == AFMT_S24_PACKED) - audio_format->reverse_endian = - G_BYTE_ORDER != G_LITTLE_ENDIAN; -#endif return true; case ERROR: @@ -485,13 +580,15 @@ oss_setup_sample_format(int fd, struct audio_format *audio_format, break; } + if (result != UNSUPPORTED) + return result == SUCCESS; + /* the requested sample format is not available - probe for other formats supported by MPD */ static const enum sample_format sample_formats[] = { SAMPLE_FORMAT_S24_P32, SAMPLE_FORMAT_S32, - SAMPLE_FORMAT_S24, SAMPLE_FORMAT_S16, SAMPLE_FORMAT_S8, SAMPLE_FORMAT_UNDEFINED /* sentinel */ @@ -503,26 +600,15 @@ oss_setup_sample_format(int fd, struct audio_format *audio_format, /* don't try that again */ continue; - oss_format = sample_format_to_oss(mpd_format); - if (oss_format == AFMT_QUERY) - /* not supported by this OSS version */ - continue; - - result = oss_try_ioctl_r(fd, SNDCTL_DSP_SAMPLESIZE, - &oss_format, msg, error_r); + result = oss_probe_sample_format(fd, mpd_format, + &mpd_format, oss_format_r, +#ifdef AFMT_S24_PACKED + export, +#endif + error_r); switch (result) { case SUCCESS: - mpd_format = sample_format_from_oss(oss_format); - if (mpd_format == SAMPLE_FORMAT_UNDEFINED) - break; - audio_format->format = mpd_format; - -#ifdef AFMT_S24_PACKED - if (oss_format == AFMT_S24_PACKED) - audio_format->reverse_endian = - G_BYTE_ORDER != G_LITTLE_ENDIAN; -#endif return true; case ERROR: @@ -533,7 +619,8 @@ oss_setup_sample_format(int fd, struct audio_format *audio_format, } } - g_set_error(error_r, oss_output_quark(), EINVAL, "%s", msg); + g_set_error_literal(error_r, oss_output_quark(), EINVAL, + "Failed to set sample format"); return false; } @@ -546,7 +633,11 @@ oss_setup(struct oss_data *od, struct audio_format *audio_format, { return oss_setup_channels(od->fd, audio_format, error_r) && oss_setup_sample_rate(od->fd, audio_format, error_r) && - oss_setup_sample_format(od->fd, audio_format, error_r); + oss_setup_sample_format(od->fd, audio_format, &od->oss_format, +#ifdef AFMT_S24_PACKED + &od->export, +#endif + error_r); } /** @@ -561,7 +652,7 @@ oss_reopen(struct oss_data *od, GError **error_r) if (od->fd < 0) { g_set_error(error_r, oss_output_quark(), errno, "Error opening OSS device \"%s\": %s", - od->device, strerror(errno)); + od->device, g_strerror(errno)); return false; } @@ -590,9 +681,8 @@ oss_reopen(struct oss_data *od, GError **error_r) } const char *const msg3 = "Failed to set sample format"; - assert(sample_format_to_oss(od->audio_format.format) != AFMT_QUERY); result = oss_try_ioctl(od->fd, SNDCTL_DSP_SAMPLESIZE, - sample_format_to_oss(od->audio_format.format), + od->oss_format, msg3, error_r); if (result != SUCCESS) { oss_close(od); @@ -606,15 +696,16 @@ oss_reopen(struct oss_data *od, GError **error_r) } 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); if (od->fd < 0) { g_set_error(error, oss_output_quark(), errno, "Error opening OSS device \"%s\": %s", - od->device, strerror(errno)); + od->device, g_strerror(errno)); return false; } @@ -628,17 +719,17 @@ oss_output_open(void *data, struct audio_format *audio_format, GError **error) } 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); } 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) { ioctl(od->fd, SNDCTL_DSP_RESET, 0); @@ -647,24 +738,33 @@ oss_output_cancel(void *data) } 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; /* reopen the device since it was closed by dropBufferedAudio */ if (od->fd < 0 && !oss_reopen(od, error)) return 0; +#ifdef AFMT_S24_PACKED + chunk = pcm_export(&od->export, chunk, size, &size); +#endif + while (true) { ret = write(od->fd, chunk, size); - if (ret > 0) - return (size_t)ret; + if (ret > 0) { +#ifdef AFMT_S24_PACKED + ret = pcm_export_source_size(&od->export, ret); +#endif + return ret; + } if (ret < 0 && errno != EINTR) { g_set_error(error, oss_output_quark(), errno, "Write error on %s: %s", - od->device, strerror(errno)); + od->device, g_strerror(errno)); return 0; } } @@ -675,6 +775,10 @@ const struct audio_output_plugin oss_output_plugin = { .test_default_device = oss_output_test_default_device, .init = oss_output_init, .finish = oss_output_finish, +#ifdef AFMT_S24_PACKED + .enable = oss_output_enable, + .disable = oss_output_disable, +#endif .open = oss_output_open, .close = oss_output_close, .play = oss_output_play, |