# include <sys/soundcard.h>
#endif
#include <sys/ioctl.h>
-#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
-#include <arpa/inet.h>
#include "mem.h"
#include "log.h"
-#include "syscalls.h"
#include "uaudio.h"
-#include "configuration.h"
-static int oss_fd = -1;
-static pthread_t oss_thread;
-static uaudio_callback *oss_callback;
-static int oss_started;
-static int oss_activated;
-static int oss_going;
-static pthread_mutex_t oss_lock = PTHREAD_MUTEX_INITIALIZER;
-static pthread_cond_t oss_cond = PTHREAD_COND_INITIALIZER;
+#ifndef AFMT_U16_NE
+# if BYTE_ORDER == BIG_ENDIAN
+# define AFMT_U16_NE AFMT_U16_BE
+# else
+# define AFMT_U16_NE AFMT_U16_LE
+# endif
+#endif
-/** @brief Buffer size in bytes */
-static int oss_bufsize;
+static int oss_fd = -1;
static const char *const oss_options[] = {
"device",
NULL
};
-static void *oss_thread_fn(void *userdata) {
- int16_t *buffer;
- pthread_mutex_lock(&oss_lock);
- buffer = xmalloc(oss_bufsize);
- while(oss_started) {
- while(oss_started && !oss_activated)
- pthread_cond_wait(&oss_cond, &oss_lock);
- if(!oss_started)
- break;
- /* We are definitely active now */
- oss_going = 1;
- pthread_cond_signal(&oss_cond);
- while(oss_activated) {
- const int nsamples = oss_callback(buffer,
- oss_bufsize / sizeof(int16_t),
- userdata);
- const int nbytes = nsamples * sizeof(int16_t);
- int written = 0;
-
- while(written < nsamples) {
- const int rc = write(oss_fd, buffer + written, nbytes - written);
- if(rc < 0)
- fatal(errno, "error playing audio");
- written += rc;
- }
- }
- oss_going = 0;
- pthread_cond_signal(&oss_cond);
- }
- pthread_mutex_unlock(&oss_lock);
- return NULL;
+/** @brief Actually play sound via OSS */
+static size_t oss_play(void *buffer, size_t samples) {
+ const size_t bytes = samples * uaudio_sample_size;
+ int rc = write(oss_fd, buffer, bytes);
+ if(rc < 0)
+ fatal(errno, "error writing to sound device");
+ return rc / uaudio_sample_size;
}
-static void oss_activate(void) {
- pthread_mutex_lock(&oss_lock);
- if(!oss_activated) {
- const char *device = uaudio_get("device");
+/** @brief Open the OSS sound device */
+static void oss_open(void) {
+ const char *device = uaudio_get("device");
#if EMPEG_HOST
- if(!device || !*device || !strcmp(device, "default")) {
- device "/dev/audio";
+ if(!device || !*device || !strcmp(device, "default"))
+ device "/dev/audio";
#else
- if(!device || !*device || !strcmp(device, "default")) {
- if(access("/dev/dsp", W_OK) == 0)
- device = "/dev/dsp";
- else
- device = "/dev/audio";
- }
-#endif
- if((oss_fd = open(device, O_WRONLY, 0)) < 0)
- fatal(errno, "error opening %s", device);
-#if EMPEG_HOST
- /* empeg audio driver only knows /dev/audio, only supports the equivalent
- * of AFMT_S16_NE, has a fixed buffer size, and does not support the
- * SNDCTL_ ioctls. */
- oss_bufsize = 4608;
-#else
- int stereo = (config->sample_format.channels == 2), format, rate;
- if(ioctl(oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
- fatal(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo);
- /* TODO we need to think about where we decide this */
- 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);
+ if(!device || !*device || !strcmp(device, "default")) {
+ if(access("/dev/dsp", W_OK) == 0)
+ device = "/dev/dsp";
else
- fatal(0, "unsupported sample_format for oss backend");
- if(ioctl(oss_fd, SNDCTL_DSP_SETFMT, &format) < 0)
- fatal(errno, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format);
- rate = config->sample_format.rate;
- if(ioctl(oss_fd, SNDCTL_DSP_SPEED, &rate) < 0)
- fatal(errno, "error calling ioctl SNDCTL_DSP_SPEED %d", rate);
- if((unsigned)rate != config->sample_format.rate)
- error(0, "asked for %luHz, got %dHz",
- (unsigned long)config->sample_format.rate, rate);
- if(ioctl(oss_fd, SNDCTL_DSP_GETBLKSIZE, &oss_bufsize) < 0) {
- error(errno, "ioctl SNDCTL_DSP_GETBLKSIZE");
- oss_bufsize = 2048; /* guess */
- }
-#endif
- oss_activated = 1;
- pthread_cond_signal(&oss_cond);
- while(!oss_going)
- pthread_cond_wait(&oss_cond, &oss_lock);
+ device = "/dev/audio";
}
- pthread_mutex_unlock(&oss_lock);
+#endif
+ if((oss_fd = open(device, O_WRONLY, 0)) < 0)
+ fatal(errno, "error opening %s", device);
+#if !EMPEG_HOST
+ int stereo = (uaudio_channels == 2), format;
+ if(ioctl(oss_fd, SNDCTL_DSP_STEREO, &stereo) < 0)
+ fatal(errno, "error calling ioctl SNDCTL_DSP_STEREO %d", stereo);
+ if(uaudio_bits == 16)
+ format = uaudio_signed ? AFMT_S16_NE : AFMT_U16_NE;
+ else
+ format = uaudio_signed ? AFMT_S8 : AFMT_U8;
+ if(ioctl(oss_fd, SNDCTL_DSP_SETFMT, &format) < 0)
+ fatal(errno, "error calling ioctl SNDCTL_DSP_SETFMT %#x", format);
+ int rate = uaudio_rate;
+ if(ioctl(oss_fd, SNDCTL_DSP_SPEED, &rate) < 0)
+ fatal(errno, "error calling ioctl SNDCTL_DSP_SPEED %d", rate);
+ if(rate != uaudio_rate)
+ error(0, "asked for %dHz, got %dHz", uaudio_rate, rate);
+#endif
+}
+
+static void oss_activate(void) {
+ oss_open();
+ uaudio_thread_activate();
}
static void oss_deactivate(void) {
- pthread_mutex_lock(&oss_lock);
- if(oss_activated) {
- oss_activated = 0;
- pthread_cond_signal(&oss_cond);
- while(oss_going)
- pthread_cond_wait(&oss_cond, &oss_lock);
- close(oss_fd);
- oss_fd = -1;
- }
- pthread_mutex_unlock(&oss_lock);
+ uaudio_thread_deactivate();
+ close(oss_fd);
+ oss_fd = -1;
}
static void oss_start(uaudio_callback *callback,
void *userdata) {
- int e;
- oss_callback = callback;
- if((e = pthread_create(&oss_thread,
- NULL,
- oss_thread_fn,
- userdata)))
- fatal(e, "pthread_create");
+ if(uaudio_channels != 1 && uaudio_channels != 2)
+ fatal(0, "asked for %d channels but only support 1 or 2",
+ uaudio_channels);
+ if(uaudio_bits != 8 && uaudio_bits != 16)
+ fatal(0, "asked for %d bits/channel but only support 8 or 16",
+ uaudio_bits);
+#if EMPEG_HOST
+ /* Very specific buffer size requirements here apparently */
+ uaudio_thread_start(callback, userdata, oss_play,
+ 4608 / uaudio_sample_size,
+ 4608 / uaudio_sample_size);
+#else
+ /* We could SNDCTL_DSP_GETBLKSIZE but only when the device is already open,
+ * which is kind of inconvenient. We go with 1-4Kbyte for now. */
+ uaudio_thread_start(callback, userdata, oss_play,
+ 32 / uaudio_sample_size,
+ 4096 / uaudio_sample_size);
+#endif
}
static void oss_stop(void) {
- void *result;
-
- oss_deactivate();
- pthread_mutex_lock(&oss_lock);
- oss_started = 0;
- pthread_cond_signal(&oss_cond);
- pthread_mutex_unlock(&oss_lock);
- pthread_join(oss_thread, &result);
+ uaudio_thread_stop();
}
const struct uaudio uaudio_oss = {