#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; }