aboutsummaryrefslogtreecommitdiffstats
path: root/src/output/oss_output_plugin.c
diff options
context:
space:
mode:
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,