From 2eb6dec8e9f0114bdbad59cf8f11f197f8fdaaf3 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 25 Apr 2007 10:27:13 +0000 Subject: initial commit git-svn-id: file:///home/lennart/svn/public/libsydney/trunk@3 9ba3c220-e4d3-45a2-8aa3-73fcc9aff6ce --- oss.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 424 insertions(+) create mode 100644 oss.c (limited to 'oss.c') diff --git a/oss.c b/oss.c new file mode 100644 index 0000000..f39571c --- /dev/null +++ b/oss.c @@ -0,0 +1,424 @@ +#include +#include +#include +#include +#include +#include + +#include "sydney.h" +#include "common.h" +#include "macro.h" +#include "malloc.h" + +#define DEFAULT_DEVICE "/dev/dsp" +#define DRIVER_NAME "oss" + +typedef struct oss_device oss_device_t; +#define OSS_DEVICE(x) ((oss_device_t*) (x)) + +struct oss_device { + sa_device_t parent; + int fd; + + unsigned real_rate; + unsigned real_nchannels; + sa_pcm_format_t real_pcm_format; +}; + +int device_create_opaque(sa_device_t **dev, const char *client_name, sa_mode_t mode, const char *codec) { + int error; + + if ((error = device_alloc_opaque(dev, sizeof(oss_device_t), client_name, mode, codec))) + return error; + + OSS_DEVICE(*dev)->fd = -1; + + return SA_SUCCESS; +} + +int device_create_pcm(sa_device_t **dev, const char *client_name, sa_mode_t mode, sa_pcm_format_t format, unsigned rate, unsigned channels) { + int error; + + if ((error = device_alloc_pcm(dev, sizeof(oss_device_t), client_name, mode, format, rate, channels))) + return error; + + OSS_DEVICE(*dev)->fd = -1; + + return SA_SUCCESS; +} + +int device_open(sa_device_t *dev) { + oss_device_t *oss = OSS_DEVICE(dev); + char *n; + int f, arg, bs, r, phase, i, found, suggested; + unsigned c; + + static const int format_map[SA_PCM_FORMAT_MAX] = { + [SA_PCM_FORMAT_U8] = AFMT_U8, + [SA_PCM_FORMAT_ULAW] = AFMT_MU_LAW, + [SA_PCM_FORMAT_ALAW] = AFMT_A_LAW, + [SA_PCM_FORMAT_S16_LE] = AFMT_S16_LE, + [SA_PCM_FORMAT_S16_BE] = AFMT_S16_BE, + [SA_PCM_FORMAT_S24_LE] = AFMT_S16_NE, /* OSS doesn't know this format, hence we pick the best we can */ + [SA_PCM_FORMAT_S24_BE] = AFMT_S16_NE, + [SA_PCM_FORMAT_S32_LE] = AFMT_S16_NE, + [SA_PCM_FORMAT_S32_BE] = AFMT_S16_NE, + [SA_PCM_FORMAT_FLOAT32_LE] = AFMT_S16_NE, + [SA_PCM_FORMAT_FLOAT32_BE] = AFMT_S16_NE + }; + static const int try_rates[] = { 8000, 16000, 32000, 44100, 48000, 96000, 192000 }; + + if (dev->driver && strcmp(dev->driver, DRIVER_NAME)) + return SA_ERROR_NO_DRIVER; + + sa_assert(oss->fd < 0); + + n = dev->device ? dev->device : DEFAULT_DEVICE; + if ((oss->fd = open(n, dev->mode == SA_MODE_RDONLY ? O_RDONLY : (dev->mode == SA_MODE_WRONLY ? O_WRONLY : O_RDWR) | O_NOCTTY | O_NONBLOCK)) < 0) { + + if (errno == ENODEV || errno == ENOENT) + return SA_ERROR_NO_DEVICE; + + return SA_ERROR_SYSTEM; + } + + if (!dev->device) { + if (!(n = sa_strdup(n))) + return SA_ERROR_OOM; + + dev->device = n; + } + + if (dev->codec) { + + if (strcmp(dev->codec, SA_CODEC_AC3) == 0) + f = AFMT_AC3; + else if (strcmp(dev->codec, SA_CODEC_MPEG) == 0) + f = AFMT_MPEG; + else + return SA_ERROR_NO_CODEC; + + } else + f = format_map[dev->pcm_format]; + + bs = 0; + + for (;;) { + arg = f; + + if (ioctl(oss->fd, SNDCTL_DSP_SETFMT, &arg) < 0) + return SA_ERROR_SYSTEM; + + if (arg == f) + break; + + /* Hmm, the device doesn't support what we're looking for, + * let's try our luck */ + + if (f == AFMT_S16_LE && !bs) { + f = AFMT_S16_BE; + bs = 1; + } else if (f == AFMT_S16_BE && !bs) { + f = AFMT_S16_LE; + bs = 1; + } else if (f == AFMT_S16_LE || f == AFMT_S16_BE) { + f = AFMT_MU_LAW; + bs = 0; + } else if (f == AFMT_MU_LAW && !bs) { + f = AFMT_A_LAW; + bs = 1; + } else if (f == AFMT_A_LAW && !bs) { + f = AFMT_MU_LAW; + bs = 1; + } else if (f == AFMT_A_LAW || f == AFMT_MU_LAW) { + f = AFMT_U8; + } else if (f == AFMT_AC3 || f == AFMT_MPEG) + return SA_ERROR_NO_CODEC; + else + return SA_ERROR_NO_PCM_FORMAT; + } + + if (!dev->codec) { + + switch (f) { + case AFMT_MU_LAW: + oss->real_pcm_format = SA_PCM_FORMAT_ULAW; + break; + + case AFMT_A_LAW: + oss->real_pcm_format = SA_PCM_FORMAT_ALAW; + break; + + case AFMT_U8: + oss->real_pcm_format = SA_PCM_FORMAT_U8; + break; + + case AFMT_S16_LE: + oss->real_pcm_format = SA_PCM_FORMAT_S16_LE; + break; + + case AFMT_S16_BE: + oss->real_pcm_format = SA_PCM_FORMAT_S16_BE; + break; + + default: + sa_assert_not_reached(); + } + + + found = 0; + + if (dev->adjust_nchannels >= 0) { + + /* First try more channels ... */ + for (c = dev->nchannels; c < 16 || c == dev->nchannels; c ++) { + arg = c; + if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0) + return SA_ERROR_SYSTEM; + + if (arg == (int) c) { + found = 1; + break; + } + } + + /* ... then try less channels */ + if (!found) { + for (c = dev->nchannels - 1; c > 0; c --) { + arg = c; + if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0) + return SA_ERROR_SYSTEM; + + if (arg == (int) c) { + found = 1; + break; + } + } + } + } else { + + /* First try less channels ... */ + for (c = dev->nchannels; c > 0; c --) { + arg = c; + if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0) + return SA_ERROR_SYSTEM; + + if (arg == (int) c) { + found = 1; + break; + } + } + + /* ... then try more channels */ + if (!found) { + for (c = dev->nchannels + 1; c < 16; c ++) { + arg = c; + if (ioctl(oss->fd, SNDCTL_DSP_CHANNELS, &arg) < 0) + return SA_ERROR_SYSTEM; + + if (arg == (int) c) { + found = 1; + break; + } + } + } + } + + if (!found) { + errno = EIO; + return SA_ERROR_SYSTEM; + } + + oss->real_nchannels = c; + + r = dev->rate; + suggested = 0; + phase = 0; + + for (;;) { + arg = r; + + if (ioctl(oss->fd, SNDCTL_DSP_SPEED, &arg) < 0) + return SA_ERROR_SYSTEM; + + sa_assert(arg > 0); + + if (arg >= r*0.95 || arg <= r *1.05) + break; + + if (arg > suggested) + suggested = arg; + + if (dev->adjust_rate >= 0) { + + if (phase == 0) { + /* Find the next higher sample rate to try */ + + for (i = 0; i < (int) elementsof(try_rates); i++) { + /* Yes, we could optimize a little here */ + + if (try_rates[i] > r) { + r = try_rates[i]; + break; + } + } + + + if (i == elementsof(try_rates)) { + phase = 1; + r = dev->rate; + } + } + + if (phase == 1) { + /* Find the next lower sample rate to try */ + + for (i = elementsof(try_rates); i > 0; i--) { + if (suggested > try_rates[i-1] && suggested < r) { + r = suggested; + break; + } else if (try_rates[i-1] < r) { + r = try_rates[i-1]; + break; + } + } + + sa_assert(i > 0); + } + + } else { + + if (phase == 0) { + /* Find the next lower sample rate to try */ + + for (i = elementsof(try_rates); i > 0; i--) { + + if (try_rates[i-1] < r) { + r = try_rates[i-1]; + break; + } + } + + if (i == 0) { + phase = 1; + r = dev->rate; + } + } + + if (phase == 1) { + /* Find the next higher sample rate to try */ + + for (i = 0; i < (int) elementsof(try_rates); i++) { + if (suggested > r && suggested < try_rates[i]) { + r = suggested; + break; + } else if (try_rates[i] < r) { + r = try_rates[i]; + break; + } + } + + sa_assert(i < (int) elementsof(try_rates)); + } + } + + } + + oss->real_rate = r; + + printf("Chosen: %u channels, %uHz, format=%u\n", oss->real_nchannels, oss->real_rate, oss->real_pcm_format); + + if (dev->adjust_nchannels != 0) + dev->nchannels = oss->real_nchannels; + if (dev->adjust_rate != 0) + dev->rate = oss->real_rate; + if (dev->pcm_format != 0) + dev->pcm_format = oss->real_pcm_format; + } + + return SA_SUCCESS; +} + +int device_destroy(sa_device_t *dev) { + oss_device_t *oss = OSS_DEVICE(dev); + + if (oss->fd >= 0) + close(oss->fd); + + device_free(dev); + return SA_SUCCESS; +} + +int device_start_thread(sa_device_t *dev, sa_event_callback_t *callback) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_change_input_volume(sa_device_t *dev, int *vol) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_change_output_volume(sa_device_t *dev, int *vol) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_change_sampling_rate(sa_device_t *dev, unsigned rate) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_get_state(sa_device_t *dev, sa_state_t *state) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_get_position(sa_device_t *dev, sa_position_t position, int64_t *pos) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_pread(sa_device_t *dev, void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_pwrite(sa_device_t *dev, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_pread_ni(sa_device_t *dev, unsigned channel, void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_pwrite_ni(sa_device_t *dev, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_get_read_size(sa_device_t *dev, size_t *size) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_get_write_size(sa_device_t *dev, size_t *size) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_resume(sa_device_t *dev) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_pause(sa_device_t *dev) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_drain(sa_device_t *dev) { + return SA_ERROR_NOT_SUPPORTED; +} + +/* Unsupported operations */ + +int device_change_device(sa_device_t *dev, const char *device_name) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_change_client_name(sa_device_t *dev, const char *client_name) { + return SA_ERROR_NOT_SUPPORTED; +} + +int device_change_stream_name(sa_device_t *dev, const char *stream_name) { + return SA_ERROR_NOT_SUPPORTED; +} -- cgit