#include #include #include #include #include #include #include "sydney.h" #include "common.h" #include "macro.h" #include "malloc.h" #include "converter.h" #include "driver.h" #define DEFAULT_DEVICE "/dev/dsp" #define DRIVER_NAME "oss" typedef struct oss_stream oss_stream_t; #define OSS_STREAM(x) ((oss_stream_t*) (x->private)) struct oss_stream { sa_stream_t *parent; int fd; pcm_attrs_t real_pcm_attrs; converter_t converter_read, converter_write; }; int driver_open(sa_stream_t *s) { oss_stream_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 (s->driver && strcmp(s->driver, DRIVER_NAME)) return SA_ERROR_NO_DRIVER; if (!(s->private = oss = sa_new0(oss_stream_t, 1))) return SA_ERROR_OOM; oss->parent = s; n = s->device ? s->device : DEFAULT_DEVICE; if ((oss->fd = open(n, s->mode == SA_MODE_RDONLY ? O_RDONLY : (s->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; } fcntl(oss->fd, F_SETFL, fcntl(oss->fd, F_GETFL) & ~O_NONBLOCK); /* FIXME*/ if (!s->device) { if (!(n = sa_strdup(n))) return SA_ERROR_OOM; s->device = n; } if (s->codec) { if (strcmp(s->codec, SA_CODEC_AC3) == 0) f = AFMT_AC3; else if (strcmp(s->codec, SA_CODEC_MPEG) == 0) f = AFMT_MPEG; else return SA_ERROR_NO_CODEC; } else f = format_map[s->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 (!s->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 (s->adjust_nchannels >= 0) { /* First try more channels ... */ for (c = s->pcm_attrs.nchannels; c < 8 || c == s->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 = s->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 = s->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 = s->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 = s->pcm_attrs.rate; if (r < 8000) r = 8000; 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 (s->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 = s->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 = s->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 (s->adjust_nchannels != 0) { sa_channel_t *cm; if (!(cm = sa_newdup(sa_channel_t, oss->real_pcm_attrs.channel_map, oss->real_pcm_attrs.nchannels))) return SA_ERROR_OOM; sa_free(s->pcm_attrs.channel_map); s->pcm_attrs.channel_map = cm; s->pcm_attrs.nchannels = oss->real_pcm_attrs.nchannels; } if (s->adjust_rate != 0) s->pcm_attrs.rate = oss->real_pcm_attrs.rate; if (s->adjust_pcm_format != 0) s->pcm_attrs.format = oss->real_pcm_attrs.format; if (s->mode & SA_MODE_RDONLY) if ((r = converter_init(&oss->converter_read, &oss->real_pcm_attrs, &s->pcm_attrs, s->dynamic_rate_enabled)) < 0) return r; if (s->mode & SA_MODE_WRONLY) if ((r = converter_init(&oss->converter_write, &s->pcm_attrs, &oss->real_pcm_attrs, s->dynamic_rate_enabled)) < 0) return r; } return SA_SUCCESS; } int driver_destroy(sa_stream_t *s) { oss_stream_t *oss = OSS_STREAM(s); 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 driver_start_thread(sa_stream_t *s, sa_event_callback_t *callback) { return SA_ERROR_NOT_SUPPORTED; } int driver_change_read_volume(sa_stream_t *s, const int32_t vol[]) { oss_stream_t *oss = OSS_STREAM(s); sa_return_val_if_fail(!s->codec, SA_ERROR_NOT_SUPPORTED); converter_set_volume(&oss->converter_read, vol); return SA_SUCCESS; } int driver_change_write_volume(sa_stream_t *s, const int32_t vol[]) { oss_stream_t *oss = OSS_STREAM(s); sa_return_val_if_fail(!s->codec, SA_ERROR_NOT_SUPPORTED); converter_set_volume(&oss->converter_write, vol); return SA_SUCCESS; } int driver_change_rate(sa_stream_t *s, unsigned rate) { oss_stream_t *oss = OSS_STREAM(s); if (s->mode & SA_MODE_RDONLY) converter_set_ratio(&oss->converter_read, oss->real_pcm_attrs.rate, s->pcm_attrs.rate); if (s->mode & SA_MODE_WRONLY) converter_set_ratio(&oss->converter_write, s->pcm_attrs.rate, oss->real_pcm_attrs.rate); return SA_SUCCESS; } int driver_get_state(sa_stream_t *s, sa_state_t *state) { return SA_ERROR_NOT_SUPPORTED; } int driver_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) { return SA_ERROR_NOT_SUPPORTED; } int driver_pread(sa_stream_t *s, void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { return SA_ERROR_NOT_SUPPORTED; } int driver_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { oss_stream_t *oss = OSS_STREAM(s); 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 driver_pread_ni(sa_stream_t *s, unsigned channel, void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { return SA_ERROR_NOT_SUPPORTED; } int driver_pwrite_ni(sa_stream_t *s, unsigned channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence) { return SA_ERROR_NOT_SUPPORTED; } int driver_get_read_size(sa_stream_t *s, size_t *size) { return SA_ERROR_NOT_SUPPORTED; } int driver_get_write_size(sa_stream_t *s, size_t *size) { return SA_ERROR_NOT_SUPPORTED; } int driver_resume(sa_stream_t *s) { return SA_ERROR_NOT_SUPPORTED; } int driver_pause(sa_stream_t *s) { return SA_ERROR_NOT_SUPPORTED; } int driver_drain(sa_stream_t *s) { oss_stream_t *oss = OSS_STREAM(s); if (ioctl(oss->fd, SNDCTL_DSP_SYNC, NULL) < 0) return SA_ERROR_SYSTEM; return SA_SUCCESS; } /* Unsupported operations */ int driver_change_device(sa_stream_t *s, const char *device) { return SA_ERROR_NOT_SUPPORTED; } int driver_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size) { return SA_ERROR_NOT_SUPPORTED; }