From c58ce0879c1cabfd31dd8657d2594d78f13b1360 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Thu, 17 May 2007 19:19:08 +0000 Subject: add handling of watermarks git-svn-id: file:///home/lennart/svn/public/libsydney/trunk@25 9ba3c220-e4d3-45a2-8aa3-73fcc9aff6ce --- common.c | 49 ++++- common.h | 1 + oss.c | 659 ++++++++++++++++++++++++++++++++++++++++----------------------- sydney.h | 4 +- 4 files changed, 474 insertions(+), 239 deletions(-) diff --git a/common.c b/common.c index 9810633..dfdde95 100644 --- a/common.c +++ b/common.c @@ -67,6 +67,7 @@ int sa_stream_create_pcm( unsigned nchannels) { int ret; + size_t lwm; sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(mode == SA_MODE_RDONLY || mode == SA_MODE_WRONLY || mode == SA_MODE_RDWR, SA_ERROR_INVALID); @@ -93,7 +94,30 @@ int sa_stream_create_pcm( if ((ret = sa_stream_change_rate(*s, rate))) goto fail; + + lwm = ((*s)->pcm_frame_size * (*s)->pcm_attrs.rate) / 20; /* 50 ms */ + + if (lwm <= 0) + lwm = (*s)->pcm_frame_size * (*s)->pcm_attrs.rate; /* 1s */ + + if (mode & SA_MODE_RDONLY) { + + if ((ret = sa_stream_set_read_lower_watermark(*s, lwm))) + goto fail; + + if ((ret = sa_stream_set_read_upper_watermark(*s, lwm*2))) + goto fail; + } + + if (mode & SA_MODE_WRONLY) { + if ((ret = sa_stream_set_write_lower_watermark(*s, lwm))) + goto fail; + + if ((ret = sa_stream_set_write_upper_watermark(*s, lwm*2))) + goto fail; + } + oil_init(); if (client_name) @@ -113,6 +137,11 @@ int sa_stream_open(sa_stream_t *s) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE); + sa_return_val_if_fail(s->codec || s->pcm_attrs.channel_map, SA_ERROR_NO_INIT); + sa_return_val_if_fail(!(s->mode & SA_MODE_RDONLY) || (s->read_lower_watermark <= s->read_upper_watermark), SA_ERROR_INVALID); + sa_return_val_if_fail(!(s->mode & SA_MODE_WRONLY) || (s->write_lower_watermark <= s->write_upper_watermark), SA_ERROR_INVALID); + sa_return_val_if_fail(!(s->mode & SA_MODE_RDONLY) || !s->codec || (s->read_lower_watermark > 0 && s->read_upper_watermark > 0), SA_ERROR_NO_INIT); + sa_return_val_if_fail(!(s->mode & SA_MODE_WRONLY) || !s->codec || (s->write_lower_watermark > 0 && s->write_upper_watermark > 0), SA_ERROR_NO_INIT); if ((ret = driver_open(s)) == 0) s->state = SA_STATE_STOPPED; @@ -256,7 +285,6 @@ int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t *callback) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(callback, SA_ERROR_INVALID); sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE); - sa_return_val_if_fail(s->codec || s->pcm_attrs.channel_map, SA_ERROR_NO_INIT); return driver_start_thread(s, callback); } @@ -396,6 +424,14 @@ int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction) { return SA_SUCCESS; } +int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction) { + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(s->state == SA_STATE_INIT, SA_ERROR_STATE); + + s->adjust_watermarks = direction; + return SA_SUCCESS; +} + int sa_stream_get_state(sa_stream_t *s, sa_state_t *state) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(state, SA_ERROR_INVALID); @@ -581,6 +617,14 @@ int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction) { return SA_SUCCESS; } +int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction) { + sa_return_val_if_fail(s, SA_ERROR_INVALID); + sa_return_val_if_fail(direction, SA_ERROR_INVALID); + + *direction = s->adjust_watermarks; + return SA_SUCCESS; +} + int sa_stream_get_user_data(sa_stream_t *s, void **value) { sa_return_val_if_fail(s, SA_ERROR_INVALID); sa_return_val_if_fail(value, SA_ERROR_INVALID); @@ -873,7 +917,8 @@ int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *dat sa_free(s->meta_data[m->idx]); s->meta_data[m->idx] = d; s->meta_data_size[m->idx] = size; - } + } else + sa_free(d); return ret; } diff --git a/common.h b/common.h index 2e838b0..41c43b1 100644 --- a/common.h +++ b/common.h @@ -45,6 +45,7 @@ struct sa_stream { sa_adjust_t adjust_rate; sa_adjust_t adjust_nchannels; sa_adjust_t adjust_pcm_format; + sa_adjust_t adjust_watermarks; sa_error_t error; sa_notify_t notify; diff --git a/oss.c b/oss.c index 50063bd..b1b1168 100644 --- a/oss.c +++ b/oss.c @@ -23,13 +23,33 @@ struct oss_stream { int fd; pcm_attrs_t real_pcm_attrs; converter_t converter_read, converter_write; + size_t read_fragment_size, write_fragment_size; + unsigned read_nfragments, write_nfragments; }; +static int simple_log2(int v) { + int k = 0; + + for (;;) { + v >>= 1; + if (!v) break; + k++; + } + + return k; +} + +static size_t fixup_bps(size_t s, size_t bps1, size_t bps2) { + return (s*bps2)/bps1; +} + int driver_open(sa_stream_t *s) { oss_stream_t *oss; char *n; - int f, arg, bs, r, phase, i, found, suggested; + int f, arg, bs, r, phase, i, found, suggested, fs, l, m; unsigned c; + size_t real_bps = 0, bps; + int loops = 0; static const int format_map[_SA_PCM_FORMAT_MAX] = { [SA_PCM_FORMAT_U8] = AFMT_U8, @@ -55,296 +75,437 @@ int driver_open(sa_stream_t *s) { 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 (!s->codec) + bps = s->pcm_frame_size * s->pcm_attrs.rate; - if (errno == ENODEV || errno == ENOENT) - return SA_ERROR_NO_DEVICE; + for (;;) { + /* We need to loop here, because we have to call SETFRAGMENT + * as first ioctl after the open, at a point where we + * don't now yet the sample type, freq and the number of + * channels we actually settled on. Hence we have to loop + * here: if the sampling format is too far off we have to call + * SETFRAGMENT again which can do only after reopening the + * device again. */ + + 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) + r = SA_ERROR_NO_DEVICE; + else + r = SA_ERROR_SYSTEM; + + goto fail; + } - 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; + fcntl(oss->fd, F_SETFL, fcntl(oss->fd, F_GETFL) & ~O_NONBLOCK); /* FIXME*/ - } else - f = format_map[s->pcm_attrs.format]; + if (!s->device) { + if (!(n = sa_strdup(n))) { + r = SA_ERROR_OOM; + goto fail; + } + + s->device = n; + } - bs = 0; - - for (;;) { - arg = f; + /* Set fragment settings */ + if (s->mode & SA_MODE_WRONLY) { + fs = s->write_upper_watermark - s->write_lower_watermark; + bs = s->write_upper_watermark; + } - 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; + if (s->mode & SA_MODE_RDONLY) { + int rfs, rbs; + + rfs = s->read_lower_watermark; + rbs = s->read_upper_watermark; + + if (s->mode & SA_MODE_WRONLY) { + fs = fs < rfs ? fs : rfs; + bs = bs > rbs ? bs : rbs; + } else { + fs = rfs; + bs = rbs; + } + } - case AFMT_U8: - oss->real_pcm_attrs.format = SA_PCM_FORMAT_U8; - break; + if (!s->codec && real_bps) { + bs = fixup_bps(bs, bps, real_bps); + fs = fixup_bps(fs, bps, real_bps); + } - case AFMT_S16_LE: - oss->real_pcm_attrs.format = SA_PCM_FORMAT_S16_LE; - break; + l = simple_log2(fs); + m = (bs+(1< 0x7FFF) m = 0x7FFF; - case AFMT_S16_BE: - oss->real_pcm_attrs.format = SA_PCM_FORMAT_S16_BE; + printf("Asking OSS for: %u fragments, %u fragsize\n", m, 1 << l); + + arg = (m << 16) | l; + + ioctl(oss->fd, SNDCTL_DSP_SETFRAGMENT, &arg); + /* We ignore errors on this call, since it's merely a hint anyway */ + + 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 { + r = SA_ERROR_NO_CODEC; + goto fail; + } + + } else + f = format_map[s->pcm_attrs.format]; + + bs = 0; + + for (;;) { + arg = f; + + if (ioctl(oss->fd, SNDCTL_DSP_SETFMT, &arg) < 0) { + r = SA_ERROR_SYSTEM; + goto fail; + } + + if (arg == f) break; - - default: - sa_assert_not_reached(); + + /* 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) { + r = SA_ERROR_NO_CODEC; + goto fail; + } else { + r = SA_ERROR_NO_PCM_FORMAT; + goto fail; + } } - - found = 0; - - if (s->adjust_nchannels >= 0) { + if (!s->codec) { - /* 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; + 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(); } - - /* ... 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; + + + 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) { + r = SA_ERROR_SYSTEM; + goto fail; + } + 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 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) { + r = SA_ERROR_SYSTEM; + goto fail; + } + + 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; + } 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) { + r = SA_ERROR_SYSTEM; + goto fail; + } + 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; + /* ... 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) { + r = SA_ERROR_SYSTEM; + goto fail; + } + + if (arg == (int) c) { + found = 1; + break; + } + } + } + } + + if (!found) { + errno = EIO; + r = SA_ERROR_SYSTEM; + goto fail; + } + + oss->real_pcm_attrs.nchannels = c; + + if (!(oss->real_pcm_attrs.channel_map = sa_new(sa_channel_t, c))) { + r = SA_ERROR_OOM; + goto fail; + } - if (ioctl(oss->fd, SNDCTL_DSP_SPEED, &arg) < 0) - return SA_ERROR_SYSTEM; + 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) { + r = SA_ERROR_SYSTEM; + goto fail; + } - sa_assert(arg > 0); + sa_assert(arg > 0); - if (arg >= r*0.95 || arg <= r *1.05) - break; + if (arg >= r*0.95 || arg <= r *1.05) + break; - if (arg > suggested) - suggested = arg; + if (arg > suggested) + suggested = arg; - if (s->adjust_rate >= 0) { + if (s->adjust_rate >= 0) { - if (phase == 0) { - /* Find the next higher sample rate to try */ + 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 */ + 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 (try_rates[i] > r) { + r = try_rates[i]; + break; + } } - } - if (i == elementsof(try_rates)) { - phase = 1; - r = s->pcm_attrs.rate; + 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; + 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); - } + sa_assert(i > 0); + } - } else { + } else { - if (phase == 0) { - /* Find the next lower sample rate to try */ + if (phase == 0) { + /* Find the next lower sample rate to try */ - for (i = elementsof(try_rates); i > 0; i--) { + for (i = elementsof(try_rates); i > 0; i--) { - if (try_rates[i-1] < r) { - r = try_rates[i-1]; - break; + if (try_rates[i-1] < r) { + r = try_rates[i-1]; + break; + } } - } - if (i == 0) { - phase = 1; - r = s->pcm_attrs.rate; + 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; + 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)); + 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); + + real_bps = oss->real_pcm_attrs.nchannels * oss->real_pcm_attrs.rate * get_pcm_sample_size(oss->real_pcm_attrs.format); + + if (real_bps != bps && loops < 1) { + loops++; + + sa_free(oss->real_pcm_attrs.channel_map); + oss->real_pcm_attrs.channel_map = NULL; + + close(oss->fd); + + printf("bps changed, retrying...\n"); + continue; + } } - oss->real_pcm_attrs.rate = r; + break; + } - printf("Chosen: %u channels, %uHz, format=%u\n", oss->real_pcm_attrs.nchannels, oss->real_pcm_attrs.rate, oss->real_pcm_attrs.format); + /* First, let's try GETBLKSIZE */ + if (ioctl(oss->fd, SNDCTL_DSP_GETBLKSIZE, &arg) >= 0) { + if (s->mode & SA_MODE_RDONLY) { + oss->read_fragment_size = arg; + oss->read_nfragments = 2; + } + + if (s->mode & SA_MODE_WRONLY) { + oss->write_fragment_size = arg; + oss->write_nfragments = 2; + } + } + + /* Now, let's use GETxSPACE */ + if (s->mode & SA_MODE_RDONLY) { + audio_buf_info info; + + if (ioctl(oss->fd, SNDCTL_DSP_GETISPACE, &info) >= 0) { + oss->read_fragment_size = info.fragsize; + oss->read_nfragments = info.fragstotal; + } + } + + if (s->mode & SA_MODE_WRONLY) { + audio_buf_info info; + + if (ioctl(oss->fd, SNDCTL_DSP_GETOSPACE, &info) >= 0) { + oss->write_fragment_size = info.fragsize; + oss->write_nfragments = info.fragstotal; + } + } + + if (s->mode & SA_MODE_WRONLY && (oss->write_fragment_size <= 0 || oss->write_nfragments <= 1)) { + errno = EIO; + r = SA_ERROR_SYSTEM; + goto fail; + } + + if (s->mode & SA_MODE_RDONLY && (oss->read_fragment_size <= 0 || oss->read_nfragments <= 1)) { + errno = EIO; + r = SA_ERROR_SYSTEM; + goto fail; + } + if (!s->codec) { + 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; + if (!(cm = sa_newdup(sa_channel_t, oss->real_pcm_attrs.channel_map, oss->real_pcm_attrs.nchannels))) { + r = SA_ERROR_OOM; + goto fail; + } sa_free(s->pcm_attrs.channel_map); s->pcm_attrs.channel_map = cm; @@ -358,26 +519,52 @@ int driver_open(sa_stream_t *s) { 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; + goto fail; 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; + goto fail; + } + + if (s->adjust_watermarks) { + + if (s->mode & SA_MODE_RDONLY) { + s->read_lower_watermark = oss->read_fragment_size; + s->read_upper_watermark = oss->read_fragment_size * oss->read_nfragments; + } + + if (s->mode & SA_MODE_WRONLY) { + s->write_lower_watermark = oss->write_fragment_size; + s->write_upper_watermark = oss->write_fragment_size * oss->write_nfragments; + } } + + if (s->mode & SA_MODE_RDONLY) + printf("Chosen for read: %u fragments, %u fragsize\n", oss->read_nfragments, oss->read_fragment_size); + if (s->mode & SA_MODE_WRONLY) + printf("Chosen for write: %u fragments, %u fragsize\n", oss->write_nfragments, oss->write_fragment_size); + return SA_SUCCESS; + +fail: + driver_destroy(s); + return r; } 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); + if (oss) { + 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; } diff --git a/sydney.h b/sydney.h index 6daf88a..30afc03 100644 --- a/sydney.h +++ b/sydney.h @@ -229,8 +229,8 @@ typedef enum { #define SA_META_ICON_NAME "sydney.icon-name" /* file name (no slashes) */ #define SA_META_ICON_PNG "sydney.icon-png" /* PNG blob */ #define SA_META_ROLE "sydney.role" /* one of: "music", "phone", "game", "event" */ -#define SA_META_X11_XID "sydney.x11-xid" /* X11 window id */ #define SA_META_X11_DISPLAY "sydney.x11-display" /* X11 display */ +#define SA_META_X11_WINDOW "sydney.x11-window" /* X11 window id */ /** Main callback function */ typedef int (*sa_event_callback_t)(sa_stream_t *s, sa_event_t event); @@ -293,6 +293,7 @@ int sa_stream_change_user_data(sa_stream_t *s, void *value); int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction); int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction); int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction); +int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction); /* Query functions */ @@ -318,6 +319,7 @@ int sa_stream_get_meta_data(sa_stream_t *s, const char *name, const void **data, int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction); int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction); int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction); +int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction); /** Get current state of the audio device */ int sa_stream_get_state(sa_stream_t *s, sa_state_t *state); -- cgit