#include #include #include #include #include #include #include "sydney.h" #include "common.h" #include "macro.h" #include "malloc.h" #include "converter.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->private)) struct oss_device { sa_device_t *parent; int fd; pcm_attrs_t real_pcm_attrs; converter_t converter_read, converter_write; }; int device_open(sa_device_t *dev) { oss_device_t *oss; 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; if (!(dev->private = oss = sa_new0(oss_device_t, 1))) return SA_ERROR_OOM; oss->parent = dev; 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_attrs.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_attrs.format = SA_PCM_FORMAT_ULAW; break; case AFMT_A_LAW: oss->real_pcm_attrs.format = SA_PCM_FORMAT_ALAW; break; case AFMT_U8: oss->real_pcm_attrs.format = SA_PCM_FORMAT_U8; break; case AFMT_S16_LE: oss->real_pcm_attrs.format = SA_PCM_FORMAT_S16_LE; break; case AFMT_S16_BE: oss->real_pcm_attrs.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->pcm_attrs.nchannels; c < 8 || c == dev->pcm_attrs.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->pcm_attrs.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->pcm_attrs.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->pcm_attrs.nchannels + 1; c < 8; 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_pcm_attrs.nchannels = c; if (!(oss->real_pcm_attrs.channel_map = sa_new(sa_channel_t, c))) return SA_ERROR_OOM; switch (c) { case 8: oss->real_pcm_attrs.channel_map[7] = SA_CHANNEL_REAR_RIGHT; case 7: oss->real_pcm_attrs.channel_map[6] = SA_CHANNEL_REAR_LEFT; case 6: oss->real_pcm_attrs.channel_map[5] = SA_CHANNEL_FRONT_LEFT; case 5: oss->real_pcm_attrs.channel_map[4] = SA_CHANNEL_FRONT_RIGHT; case 4: oss->real_pcm_attrs.channel_map[3] = SA_CHANNEL_LFE; case 3: oss->real_pcm_attrs.channel_map[2] = SA_CHANNEL_CENTER; case 2: oss->real_pcm_attrs.channel_map[1] = SA_CHANNEL_RIGHT; oss->real_pcm_attrs.channel_map[0] = SA_CHANNEL_LEFT; break; case 1: oss->real_pcm_attrs.channel_map[0] = SA_CHANNEL_MONO; break; } r = dev->pcm_attrs.rate; r = 44100; 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->pcm_attrs.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->pcm_attrs.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_pcm_attrs.rate = r; printf("Chosen: %u channels, %uHz, format=%u\n", oss->real_pcm_attrs.nchannels, oss->real_pcm_attrs.rate, oss->real_pcm_attrs.format); if (dev->adjust_nchannels != 0) dev->pcm_attrs.nchannels = oss->real_pcm_attrs.nchannels; if (dev->adjust_rate != 0) dev->pcm_attrs.rate = oss->real_pcm_attrs.rate; if (dev->adjust_pcm_format != 0) dev->pcm_attrs.format = oss->real_pcm_attrs.format; if (dev->mode & SA_MODE_RDONLY) if ((r = converter_init(&oss->converter_read, &oss->real_pcm_attrs, &dev->pcm_attrs, dev->dynamic_rate_enabled)) < 0) return r; if (dev->mode & SA_MODE_WRONLY) if ((r = converter_init(&oss->converter_write, &dev->pcm_attrs, &oss->real_pcm_attrs, dev->dynamic_rate_enabled)) < 0) return r; } return SA_SUCCESS; } int device_destroy(sa_device_t *dev) { oss_device_t *oss = OSS_DEVICE(dev); if (oss->fd >= 0) close(oss->fd); sa_free(oss->real_pcm_attrs.channel_map); converter_done(&oss->converter_read); converter_done(&oss->converter_write); sa_free(oss); 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[]) { oss_device_t *oss = OSS_DEVICE(dev); sa_return_val_if_fail(!dev->codec, SA_ERROR_NOT_SUPPORTED); converter_set_volume(&oss->converter_read, vol); return SA_SUCCESS; } int device_change_output_volume(sa_device_t *dev, int vol[]) { oss_device_t *oss = OSS_DEVICE(dev); sa_return_val_if_fail(!dev->codec, SA_ERROR_NOT_SUPPORTED); converter_set_volume(&oss->converter_write, vol); return SA_SUCCESS; } int device_change_rate(sa_device_t *dev, unsigned rate) { oss_device_t *oss = OSS_DEVICE(dev); converter_set_ratio(&oss->converter_read, oss->real_pcm_attrs.rate, dev->pcm_attrs.rate); converter_set_ratio(&oss->converter_write, dev->pcm_attrs.rate, oss->real_pcm_attrs.rate); return SA_SUCCESS; } 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) { oss_device_t *oss = OSS_DEVICE(dev); void **dst; size_t *stride; int ret; uint8_t *d; if ((ret = converter_go_interleaved(&oss->converter_write, data, &dst, &stride, 1, &nbytes))) return ret; d = dst[0]; while (nbytes > 0) { ssize_t l; if ((l = write(oss->fd, d, nbytes)) < 0) return SA_ERROR_SYSTEM; sa_assert(l > 0); nbytes -= l; d += l; } return SA_SUCCESS; } 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) { oss_device_t *oss = OSS_DEVICE(dev); if (ioctl(oss->fd, SNDCTL_DSP_SYNC, NULL) < 0) return SA_ERROR_SYSTEM; return SA_SUCCESS; } /* Unsupported operations */ int device_change_device(sa_device_t *dev) { return SA_ERROR_NOT_SUPPORTED; } int device_change_meta_data(sa_device_t *dev, const char *name, const void *data, size_t size) { return SA_ERROR_NOT_SUPPORTED; }