summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common.c49
-rw-r--r--common.h1
-rw-r--r--oss.c659
-rw-r--r--sydney.h4
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<<l)-1)/(1<<l);
+ if (l < 1) l = 1;
+ if (m < 2) m = 2;
+ if (m > 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);