From e99d42b153659e7ac644bb93700acb81514998e5 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Thu, 4 Oct 2007 12:41:37 +0100 Subject: [PATCH] OSS support in speaker Organization: Straylight/Edgeware From: rjk@greenend.org.uk <> --- doc/disorder_config.5.in | 7 +- lib/configuration.c | 21 +++-- lib/configuration.h | 1 + plugins/exec.c | 2 +- server/Makefile.am | 1 + server/disorderd.c | 3 +- server/normalize.c | 8 +- server/play.c | 21 +++-- server/speaker-oss.c | 168 +++++++++++++++++++++++++++++++++++++++ server/speaker.c | 3 + server/speaker.h | 1 + 11 files changed, 217 insertions(+), 19 deletions(-) create mode 100644 server/speaker-oss.c diff --git a/doc/disorder_config.5.in b/doc/disorder_config.5.in index 1f0b266..26b1efc 100644 --- a/doc/disorder_config.5.in +++ b/doc/disorder_config.5.in @@ -1,3 +1,4 @@ + .\" .\" Copyright (C) 2004, 2005, 2006, 2007 Richard Kettlewell .\" @@ -349,7 +350,7 @@ The default is With the .B network backend the sample format is forced to -.B 16/44100/2b +.B 16b/44100/2 and with the .B coreaudio backend it is forced to @@ -377,6 +378,10 @@ default. Use Apple Core Audio. This only available on OS X systems, on which it is the default. .TP +.B oss +Use the OSS (/dev/dsp) API. Not available on all platforms. Not well +maintained at the moment. +.TP .B command Execute a command. This is the default if .B speaker_command diff --git a/lib/configuration.c b/lib/configuration.c index 448a8ab..92acce1 100644 --- a/lib/configuration.c +++ b/lib/configuration.c @@ -457,6 +457,14 @@ static int set_backend(const struct config_state *cs, error(0, "%s:%d: Core Audio is not available on this platform", cs->path, cs->line); return -1; +#endif + } else if(!strcmp(vec[0], "oss")) { +#if HAVE_SYS_SOUNDCARD_H + *valuep = BACKEND_OSS; +#else + error(0, "%s:%d: OSS is not available on this platform", + cs->path, cs->line); + return -1; #endif } else { error(0, "%s:%d: invalid '%s' value '%s'", @@ -1091,20 +1099,21 @@ static void config_postdefaults(struct config *c, if(c->speaker_backend == BACKEND_NETWORK && !c->broadcast.n) fatal(0, "speaker_backend is network but broadcast is not set"); } - if(c->speaker_backend == BACKEND_NETWORK) { - /* Override sample format */ + /* Override sample format */ + switch(c->speaker_backend) { + case BACKEND_NETWORK: c->sample_format.rate = 44100; c->sample_format.channels = 2; c->sample_format.bits = 16; c->sample_format.endian = ENDIAN_BIG; - } - if(c->speaker_backend == BACKEND_COREAUDIO) { - /* Override sample format */ + break; + case BACKEND_COREAUDIO: c->sample_format.rate = 44100; c->sample_format.channels = 2; c->sample_format.bits = 16; c->sample_format.endian = ENDIAN_NATIVE; - } + break; + } } /** @brief (Re-)read the config file diff --git a/lib/configuration.h b/lib/configuration.h index e4ac7b9..4dad503 100644 --- a/lib/configuration.h +++ b/lib/configuration.h @@ -183,6 +183,7 @@ struct config { #define BACKEND_COMMAND 1 /**< Execute a command */ #define BACKEND_NETWORK 2 /**< Transmit RTP */ #define BACKEND_COREAUDIO 3 /**< Use Core Audio (Mac only) */ +#define BACKEND_OSS 4 /**< Use OSS */ /** @brief Home directory for state files */ const char *home; diff --git a/plugins/exec.c b/plugins/exec.c index fb01df7..7584809 100644 --- a/plugins/exec.c +++ b/plugins/exec.c @@ -46,7 +46,7 @@ void disorder_play_track(const char *const *parameters, vec[j++] = path; vec[j] = 0; execvp(vec[0], (char **)vec); - disorder_fatal(errno, "error executing %s", vec[0]); + disorder_fatal(errno, "executing %s", vec[0]); } /* diff --git a/server/Makefile.am b/server/Makefile.am index 1aef562..8fc3dfe 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -48,6 +48,7 @@ disorder_speaker_SOURCES=speaker.c speaker.h \ speaker-command.c \ speaker-network.c \ speaker-coreaudio.c \ + speaker-oss.c \ speaker-alsa.c disorder_speaker_LDADD=$(LIBOBJS) ../lib/libdisorder.a \ $(LIBASOUND) $(LIBPCRE) $(LIBICONV) $(LIBGCRYPT) $(COREAUDIO) diff --git a/server/disorderd.c b/server/disorderd.c index b41dade..f4a1a62 100644 --- a/server/disorderd.c +++ b/server/disorderd.c @@ -186,7 +186,8 @@ static void volumecheck_after(long offset) { /* We fix the path to include the bindir and sbindir we were installed into */ static void fix_path(void) { char *path = getenv("PATH"); - char *newpath; + static char *newpath; + /* static or libgc collects it! */ if(!path) error(0, "PATH is not set at all!"); diff --git a/server/normalize.c b/server/normalize.c index 073b869..8ea6efa 100644 --- a/server/normalize.c +++ b/server/normalize.c @@ -134,9 +134,11 @@ int main(int argc, char attribute((unused)) **argv) { if(r < 0) { if(errno != EINTR) fatal(errno, "error reading header"); - } else if(r == 0) - fatal(0, "EOF reading header"); - else + } else if(r == 0) { + if(n) + fatal(0, "EOF reading header"); + break; + } else n += r; } /* Sanity check the header */ diff --git a/server/play.c b/server/play.c index 35d756b..1ca6434 100644 --- a/server/play.c +++ b/server/play.c @@ -343,8 +343,11 @@ static int start(ev_source *ev, } /* Use the second arg as the tag if available (it's probably a command name), * otherwise the module name. */ - lfd = logfd(ev, (config->player.s[n].s[2] - ? config->player.s[n].s[2] : config->player.s[n].s[1])); + if(!isatty(2)) + lfd = logfd(ev, (config->player.s[n].s[2] + ? config->player.s[n].s[2] : config->player.s[n].s[1])); + else + lfd = -1; optc = config->player.s[n].n - 2; optv = (void *)&config->player.s[n].s[2]; while(optc > 0 && optv[0][0] == '-') { @@ -371,9 +374,11 @@ static int start(ev_source *ev, exitfn = _exit; ev_signal_atfork(ev); signal(SIGPIPE, SIG_DFL); - xdup2(lfd, 1); - xdup2(lfd, 2); - xclose(lfd); /* tidy up */ + if(lfd != -1) { + xdup2(lfd, 1); + xdup2(lfd, 2); + xclose(lfd); /* tidy up */ + } setpgid(0, 0); if((q->type & DISORDER_PLAYER_TYPEMASK) == DISORDER_PLAYER_RAW) { /* "Raw" format players always have their output send down a pipe @@ -467,11 +472,13 @@ static int start(ev_source *ev, error(errno, "error calling fork"); if(q->type & DISORDER_PLAYER_PREFORK) play_cleanup(q->pl, q->data); /* else would leak */ - xclose(lfd); + if(lfd != -1) + xclose(lfd); return START_SOFTFAIL; } store_player_pid(q->id, pid); - xclose(lfd); + if(lfd != -1) + xclose(lfd); setpgid(pid, pid); ev_child(ev, pid, 0, player_finished, q); D(("player subprocess ID %lu", (unsigned long)pid)); diff --git a/server/speaker-oss.c b/server/speaker-oss.c new file mode 100644 index 0000000..f9a298c --- /dev/null +++ b/server/speaker-oss.c @@ -0,0 +1,168 @@ +/* + * This file is part of DisOrder + * Copyright (C) 2007 Richard Kettlewell + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ +/** @file server/speaker-oss.c + * @brief Support for @ref BACKEND_OSS */ + +#include + +#if HAVE_SYS_SOUNDCARD_H + +#include "types.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "configuration.h" +#include "syscalls.h" +#include "log.h" +#include "speaker-protocol.h" +#include "speaker.h" + +/** @brief Current device */ +static int ossfd = -1; + +/** @brief OSS backend initialization */ +static void oss_init(void) { + info("selected OSS backend"); +} + +/** @brief OSS deactivation */ +static void oss_deactivate(void) { + if(ossfd != -1) { + xclose(ossfd); + ossfd = -1; + device_state = device_closed; + D(("released audio device")); + } +} + +/** @brief OSS backend activation */ +static void oss_activate(void) { + int stereo, format, rate; + const char *device; + + if(ossfd == -1) { + /* Try to pick a device */ + if(!strcmp(config->device, "default")) { + if(access("/dev/dsp", W_OK) == 0) + device = "/dev/dsp"; + else if(access("/dev/audio", W_OK) == 0) + device = "/dev/audio"; + } else + device = config->device; /* just believe the user */ + /* Open the device */ + if((ossfd = open(device, O_WRONLY, 0)) < 0) { + error(errno, "error opening %s", device); + goto failed; + } + /* Set the audio format */ + stereo = (config->sample_format.channels == 2); + if(ioctl(ossfd, SNDCTL_DSP_STEREO, &stereo) < 0) { + error(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo); + goto failed; + } + if(config->sample_format.bits == 8) + format = AFMT_U8; + else if(config->sample_format.bits == 16) + format = (config->sample_format.endian == ENDIAN_LITTLE + ? AFMT_S16_LE : AFMT_S16_BE); + else { + error(0, "unsupported sample_format for oss backend"); + goto failed; + } + if(ioctl(ossfd, SNDCTL_DSP_SETFMT, &format) < 0) { + error(errno, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format); + goto failed; + } + rate = config->sample_format.rate; + if(ioctl(ossfd, SNDCTL_DSP_SPEED, &rate) < 0) { + error(errno, "error calling ioctl SNDCTL_DSP_SPEED %d", rate); + goto failed; + } + if((unsigned)rate != config->sample_format.rate) + error(0, "asked for %luHz, got %dHz", + (unsigned long)config->sample_format.rate, rate); + nonblock(ossfd); + device_state = device_open; + } + return; +failed: + device_state = device_error; + if(ossfd >= 0) + xclose(ossfd); +} + +/** @brief Play via OSS */ +static size_t oss_play(size_t frames) { + const size_t bytes_to_play = frames * bpf; + ssize_t bytes_written; + + bytes_written = write(ossfd, playing->buffer + playing->start, + bytes_to_play); + if(bytes_written < 0) + switch(errno) { + case EINTR: /* interruped */ + case EAGAIN: /* overrun */ + return 0; /* try again later */ + default: + fatal(errno, "error writing to audio device"); + } + return bytes_written / bpf; +} + +static int oss_slot; + +/** @brief Fill in poll fd array for OSS */ +static void oss_beforepoll(void) { + oss_slot = addfd(ossfd, POLLOUT|POLLERR); +} + +/** @brief Process poll() results for OSS */ +static int oss_ready(void) { + return !!(fds[oss_slot].revents & (POLLOUT|POLLERR)); +} + +const struct speaker_backend oss_backend = { + BACKEND_OSS, + 0, + oss_init, + oss_activate, + oss_play, + oss_deactivate, + oss_beforepoll, + oss_ready +}; + +#endif + +/* +Local Variables: +c-basic-offset:2 +comment-column:40 +fill-column:79 +indent-tabs-mode:nil +End: +*/ diff --git a/server/speaker.c b/server/speaker.c index 75e9cd7..7f4578d 100644 --- a/server/speaker.c +++ b/server/speaker.c @@ -379,6 +379,9 @@ static const struct speaker_backend *backends[] = { &network_backend, #if HAVE_COREAUDIO_AUDIOHARDWARE_H &coreaudio_backend, +#endif +#if HAVE_SYS_SOUNDCARD_H + &oss_backend, #endif 0 }; diff --git a/server/speaker.h b/server/speaker.h index 4e22d38..061da0b 100644 --- a/server/speaker.h +++ b/server/speaker.h @@ -213,6 +213,7 @@ extern const struct speaker_backend network_backend; extern const struct speaker_backend alsa_backend; extern const struct speaker_backend command_backend; extern const struct speaker_backend coreaudio_backend; +extern const struct speaker_backend oss_backend; extern struct pollfd fds[NFDS]; extern int fdno; -- [mdw]