else
AC_MSG_WARN([no support for id3v2 tags])
fi
+########################################################################### oss
+OLD_CPPFLAGS="$CPPFLAGS"
+OLD_LD_FLAGS="$LDFLAGS"
+OLD_LIBS="$LIBS"
+
+have_oss="yes"
+msg="=> will not build para_fade/oss writer"
+
+AC_CHECK_HEADER(sys/soundcard.h, [
+ extras="$extras para_fade"
+ all_executables="$all_executables fade"
+ all_errlist_objs="$all_errlist_objs oss_write"
+ audiod_errlist_objs="$audiod_errlist_objs oss_write"
+ audiod_cmdline_objs="$audiod_cmdline_objs oss_write.cmdline"
+
+ write_errlist_objs="$write_errlist_objs oss_write"
+ write_cmdline_objs="$write_cmdline_objs oss_write.cmdline"
+ writers="$writers oss"
+ default_writer="OSS_WRITE"
+ AC_CHECK_LIB(ossaudio, _oss_ioctl, [
+ audiod_ldflags="$audiod_ldflags -lossaudio"
+ write_ldflags="$write_ldflags -lossaudio"
+ fade_ldflags="$write_ldflags -lossaudio"
+ ]
+ )
+ ],
+ [
+ have_oss="no"
+ AC_MSG_WARN([no linux/soundcard.h $msg])
+ ]
+)
+CPPFLAGS="$OLD_CPPFLAGS"
+LDFLAGS="$OLD_LDFLAGS"
+LIBS="$OLD_LIBS"
+
########################################################################### alsa
-have_alsa="yes"
OLD_CPPFLAGS="$CPPFLAGS"
OLD_LD_FLAGS="$LDFLAGS"
OLD_LIBS="$LIBS"
+
+msg="=> no alsa support for para_audiod/para_write"
if test "$OSTYPE" != "Linux"; then
have_alsa="no"
+else
+ have_alsa="yes"
fi
-msg="=> will not build para_fade"
-if test "$have_alsa" = "yes"; then
- AC_CHECK_HEADER(linux/soundcard.h, [
- extras="$extras para_fade"
- all_executables="$all_executables fade"
- ],
- [
- have_alsa="no"
- AC_MSG_WARN([no linux/soundcard.h $msg])
- ]
- )
-fi
-msg="=> no alsa support for para_audiod/para_write"
if test "$have_alsa" = "yes"; then
AC_CHECK_HEADERS([alsa/asoundlib.h], [], [
have_alsa="no"
writers="$writers alsa"
default_writer="ALSA_WRITE"
fi
+
CPPFLAGS="$OLD_CPPFLAGS"
LDFLAGS="$OLD_LDFLAGS"
LIBS="$OLD_LIBS"
objlist_to_errlist($gui_errlist_objs), errors used by para_gui)
AC_SUBST(fade_objs, add_dot_o($fade_objs))
+AC_SUBST(fade_ldflags, $fade_ldflags)
AC_DEFINE_UNQUOTED(INIT_FADE_ERRLISTS,
objlist_to_errlist($fade_errlist_objs), errors used by para_fade)
--- /dev/null
+/*
+ * Copyright (C) 2009 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file oss_write.c Paraslash's oss output plugin. */
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/soundcard.h>
+
+#include "para.h"
+#include "fd.h"
+#include "string.h"
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "write.h"
+#include "oss_write.cmdline.h"
+#include "error.h"
+
+/** Always use 16 bit little endian. */
+#define FORMAT AFMT_S16_LE
+
+/** Data specific to the oss writer. */
+struct private_oss_write_data {
+ /** The file handle of the device. */
+ int fd;
+ /**
+ * The samplerate given by command line option or the decoder
+ * of the writer node group.
+ */
+ int samplerate;
+ /**
+ * The number of channels, given by command line option or the
+ * decoder of the writer node group.
+ */
+ int channels;
+ /** Four bytes for stereo streams, two bytes for mono streams. */
+ int bytes_per_frame;
+};
+
+static int oss_pre_select(struct sched *s, struct writer_node *wn)
+{
+ struct private_oss_write_data *powd = wn->private_data;
+ struct writer_node_group *wng = wn->wng;
+
+ if (!*wng->loaded)
+ return 0;
+ para_fd_set(powd->fd, &s->wfds, &s->max_fileno);
+ return 1;
+}
+static int oss_post_select(__a_unused struct sched *s,
+ struct writer_node *wn)
+{
+ int ret;
+ struct private_oss_write_data *powd = wn->private_data;
+ struct writer_node_group *wng = wn->wng;
+ size_t frames, bytes = *wng->loaded - wn->written;
+ char *data = *wng->bufp + wn->written;
+
+ if (*wng->input_error < 0 && bytes < powd->bytes_per_frame) {
+ wn->written = *wng->loaded;
+ return *wng->input_error;
+ }
+ frames = bytes / powd->bytes_per_frame;
+ if (!frames) /* less than a single frame available */
+ goto out;
+ if (!FD_ISSET(powd->fd, &s->wfds))
+ goto out;
+ ret = write_nonblock(powd->fd, data, frames * powd->bytes_per_frame, 0);
+ if (ret < 0)
+ return ret;
+ wn->written += ret;
+out:
+ return 1;
+}
+
+static void oss_close(struct writer_node *wn)
+{
+ struct private_oss_write_data *powd = wn->private_data;
+
+ close(powd->fd);
+ free(powd);
+}
+
+/*
+ * The Open Sound System Programmer's Guide sayeth:
+ *
+ * Set sampling parameters always so that number of channels (mono/stereo) is
+ * set before selecting sampling rate (speed). Failing to do this will make
+ * your program incompatible with cards such as the SoundBlaster Pro which
+ * supports 44.1 kHz in mono but just 22.05 kHz in stereo. A program which
+ * selects 44.1 kHz speed and then sets the device to stereo mode will
+ * incorrectly believe that the device is still in 44.1 kHz mode when actually
+ * the speed is decreased to 22.05 kHz.
+ */
+static int oss_open(struct writer_node *wn)
+{
+ int ret, format = FORMAT, channels, samplerate;
+ struct oss_write_args_info *conf = wn->conf;
+ struct writer_node_group *wng = wn->wng;
+ struct private_oss_write_data *powd;
+
+ PARA_INFO_LOG("opening %s\n", conf->device_arg);
+ ret = para_open(conf->device_arg, O_WRONLY, 0);
+ if (ret < 0)
+ return ret;
+ powd = para_calloc(sizeof(*powd));
+ wn->private_data = powd;
+ powd->fd = ret;
+ ret = mark_fd_nonblocking(powd->fd);
+ if (ret < 0)
+ goto err;
+ /* set PCM format */
+ ret = ioctl(powd->fd, SNDCTL_DSP_SETFMT, &format);
+ if (ret < 0) {
+ ret = -ERRNO_TO_PARA_ERROR(errno);
+ goto err;
+ }
+ ret = -E_BAD_SAMPLE_FORMAT;
+ if (format != FORMAT)
+ goto err;
+ /* set number of channels */
+ if (!conf->channels_given && wng->channels)
+ channels = *wng->channels;
+ else
+ channels = conf->channels_arg;
+ ret = -E_BAD_CHANNEL_COUNT;
+ if (channels == 0)
+ goto err;
+ powd->channels = channels;
+ ret = ioctl(powd->fd, SNDCTL_DSP_CHANNELS, &channels);
+ if (ret < 0) {
+ ret = -ERRNO_TO_PARA_ERROR(errno);
+ goto err;
+ }
+ if (powd->channels != channels)
+ goto err;
+ powd->bytes_per_frame = channels * 2;
+
+ /*
+ * Set sampling rate
+ *
+ * If we request a higher sampling rate than is supported by the
+ * device, the the highest possible speed is automatically used. The
+ * value actually used is returned as the new value of the argument.
+ */
+ if (!conf->samplerate_given && wng->samplerate)
+ samplerate = *wng->samplerate;
+ else
+ samplerate = conf->samplerate_arg;
+ powd->samplerate = samplerate;
+ ret = ioctl(powd->fd, SNDCTL_DSP_SPEED, &samplerate);
+ if (ret < 0) {
+ ret = -ERRNO_TO_PARA_ERROR(errno);
+ goto err;
+ }
+ if (samplerate != powd->samplerate) {
+ int min = PARA_MIN(samplerate, powd->samplerate),
+ max = PARA_MAX(samplerate, powd->samplerate);
+ /*
+ * Check whether the returned sample rate differs significantly
+ * from the requested one.
+ */
+ ret = -E_BAD_SAMPLERATE;
+ if (100 * max > 110 * min) /* more than 10% deviation */
+ goto err;
+ PARA_NOTICE_LOG("using %dHz rather than %dHz\n", samplerate,
+ powd->samplerate);
+ }
+
+ return 1;
+err:
+ close(powd->fd);
+ free(powd);
+ return ret;
+}
+
+__malloc static void *oss_parse_config(const char *options)
+{
+ int ret;
+ struct oss_write_args_info *conf = para_calloc(sizeof(*conf));
+
+ PARA_INFO_LOG("options: %s, %zd\n", options, strcspn(options, " \t"));
+ ret = oss_cmdline_parser_string(options, conf, "oss_write");
+ if (ret)
+ goto err_out;
+ return conf;
+err_out:
+ free(conf);
+ return NULL;
+}
+
+/**
+ * The init function of the oss writer.
+ *
+ * \param w Pointer to the writer to initialize.
+ *
+ * \sa struct writer.
+ */
+void oss_write_init(struct writer *w)
+{
+ struct oss_write_args_info dummy;
+
+ oss_cmdline_parser_init(&dummy);
+ w->open = oss_open;
+ w->close = oss_close;
+ w->pre_select = oss_pre_select;
+ w->post_select = oss_post_select;
+ w->parse_config = oss_parse_config;
+ w->shutdown = NULL;
+ w->help = (struct ggo_help) {
+ .short_help = oss_write_args_info_help,
+ .detailed_help = oss_write_args_info_detailed_help
+ };
+}