pa_stream *stream;
pa_sample_spec ss;
- unsigned int frame_size;
+ size_t frame_size;
pa_buffer_attr buffer_attr;
} snd_pcm_pulse_t;
-static void update_ptr(snd_pcm_pulse_t * pcm)
+static int update_ptr(snd_pcm_pulse_t *pcm)
{
size_t size;
if (pcm->io.stream == SND_PCM_STREAM_PLAYBACK)
size = pa_stream_writable_size(pcm->stream);
else
- size = pa_stream_readable_size(pcm->stream) - pcm->offset;
+ size = pa_stream_readable_size(pcm->stream);
+
+ if (size == (size_t) -1)
+ return -EIO;
+
+ if (pcm->io.stream == SND_PCM_STREAM_CAPTURE)
+ size -= pcm->offset;
if (size > pcm->last_size) {
pcm->ptr += size - pcm->last_size;
}
pcm->last_size = size;
+ return 0;
+ }
+
+static int check_active(snd_pcm_pulse_t *pcm) {
+ assert(pcm);
+
+ /*
+ * ALSA thinks in periods, not bytes, samples or frames.
+ */
+
+ if (pcm->io.stream == SND_PCM_STREAM_PLAYBACK) {
+ size_t wsize;
+
+ wsize = pa_stream_writable_size(pcm->stream);
+
+ if (wsize == (size_t) -1)
+ return -EIO;
+
+ return wsize >= pcm->buffer_attr.minreq;
+ } else {
+ size_t rsize;
+
+ rsize = pa_stream_readable_size(pcm->stream);
+
+ if (rsize == (size_t) -1)
+ return -EIO;
+
+ return rsize >= pcm->buffer_attr.fragsize;
+ }
+}
+
+static int update_active(snd_pcm_pulse_t *pcm) {
+ int ret;
+
+ assert(pcm);
+
+ ret = check_active(pcm);
+ if (ret < 0)
+ return ret;
+
+ if (ret > 0)
+ pulse_poll_activate(pcm->p);
+ else
+ pulse_poll_deactivate(pcm->p);
+
+ return 0;
}
static int pulse_start(snd_pcm_ioplug_t * io)
assert(pcm);
assert(pcm->p);
- if (pcm->stream == NULL)
- return 0;
-
pa_threaded_mainloop_lock(pcm->p->mainloop);
err = pulse_check_connection(pcm->p);
if (err < 0)
goto finish;
+ if (pcm->stream == NULL)
+ goto finish;
+
o = pa_stream_cork(pcm->stream, 0, pulse_stream_success_cb,
pcm->p);
if (!o) {
goto finish;
}
+ pcm->underrun = 0;
err_o = pulse_wait_operation(pcm->p, o);
err_u = pulse_wait_operation(pcm->p, u);
if (err_o < 0 || err_u < 0) {
err = -EIO;
goto finish;
- } else
- pcm->underrun = 0;
+ }
- finish:
+finish:
pa_threaded_mainloop_unlock(pcm->p->mainloop);
return err;
static int pulse_stop(snd_pcm_ioplug_t * io)
{
snd_pcm_pulse_t *pcm = io->private_data;
- pa_operation *o;
- int err = 0;
+ pa_operation *o, *u;
+ int err = 0, err_o = 0, err_u = 0;
assert(pcm);
assert(pcm->p);
if (err < 0)
goto finish;
- /* If stream connection fails, this gets called anyway */
if (pcm->stream == NULL)
goto finish;
- o = pa_stream_flush(pcm->stream, pulse_stream_success_cb, pcm->p);
+ o = pa_stream_cork(pcm->stream, 1, pulse_stream_success_cb,
+ pcm->p);
if (!o) {
err = -EIO;
goto finish;
}
- err = pulse_wait_operation(pcm->p, o);
-
- pa_operation_unref(o);
-
- if (err < 0) {
- err = -EIO;
- goto finish;
- }
-
- o = pa_stream_cork(pcm->stream, 1, pulse_stream_success_cb,
- pcm->p);
- if (!o) {
+ u = pa_stream_flush(pcm->stream, pulse_stream_success_cb,
+ pcm->p);
+ if (!u) {
+ pa_operation_unref(o);
err = -EIO;
goto finish;
}
- err = pulse_wait_operation(pcm->p, o);
+ err_o = pulse_wait_operation(pcm->p, o);
+ err_u = pulse_wait_operation(pcm->p, u);
pa_operation_unref(o);
+ pa_operation_unref(u);
- if (err < 0) {
+ if (err_o < 0 || err_u < 0) {
err = -EIO;
goto finish;
}
- finish:
+finish:
pa_threaded_mainloop_unlock(pcm->p->mainloop);
return err;
pa_threaded_mainloop_lock(pcm->p->mainloop);
- assert(pcm->stream);
-
err = pulse_check_connection(pcm->p);
if (err < 0)
goto finish;
goto finish;
}
- finish:
+finish:
pa_threaded_mainloop_unlock(pcm->p->mainloop);
return err;
static snd_pcm_sframes_t pulse_pointer(snd_pcm_ioplug_t * io)
{
snd_pcm_pulse_t *pcm = io->private_data;
- int err = 0;
+ snd_pcm_sframes_t ret = 0;
assert(pcm);
assert(pcm->p);
+ if (io->state == SND_PCM_STATE_XRUN)
+ return -EPIPE;
+
+ if (io->state != SND_PCM_STATE_RUNNING)
+ return 0;
+
pa_threaded_mainloop_lock(pcm->p->mainloop);
assert(pcm->stream);
- err = pulse_check_connection(pcm->p);
- if (err < 0)
+ ret = pulse_check_connection(pcm->p);
+ if (ret < 0)
goto finish;
- update_ptr(pcm);
+ if (pcm->underrun) {
+ ret = -EPIPE;
+ goto finish;
+ }
- err = snd_pcm_bytes_to_frames(io->pcm, pcm->ptr);
+ ret = update_ptr(pcm);
+ if (ret < 0) {
+ ret = -EPIPE;
+ goto finish;
+ }
if (pcm->underrun)
- err = -EPIPE;
+ ret = -EPIPE;
+ else
+ ret = snd_pcm_bytes_to_frames(io->pcm, pcm->ptr);
+
+finish:
- finish:
pa_threaded_mainloop_unlock(pcm->p->mainloop);
- return err;
+ return ret;
}
static int pulse_delay(snd_pcm_ioplug_t * io, snd_pcm_sframes_t * delayp)
{
snd_pcm_pulse_t *pcm = io->private_data;
int err = 0;
- pa_usec_t lat;
+ pa_usec_t lat = 0;
assert(pcm);
assert(pcm->p);
assert(pcm->stream);
- err = pulse_check_connection(pcm->p);
- if (err < 0)
- goto finish;
+ for (;;) {
+ err = pulse_check_connection(pcm->p);
+ if (err < 0)
+ goto finish;
- if (pa_stream_get_latency(pcm->stream, &lat, NULL)) {
- err = -EIO;
- goto finish;
+ err = pa_stream_get_latency(pcm->stream, &lat, NULL);
+ if (err) {
+ if (err != PA_ERR_NODATA) {
+ err = -EIO;
+ goto finish;
+ }
+ } else
+ break;
+
+ pa_threaded_mainloop_wait(pcm->p->mainloop);
}
*delayp =
snd_pcm_bytes_to_frames(io->pcm,
pa_usec_to_bytes(lat, &pcm->ss));
+ err = 0;
+
+finish:
+
if (pcm->underrun && pcm->io.state == SND_PCM_STATE_RUNNING)
snd_pcm_ioplug_set_state(io, SND_PCM_STATE_XRUN);
- finish:
pa_threaded_mainloop_unlock(pcm->p->mainloop);
return err;
{
snd_pcm_pulse_t *pcm = io->private_data;
const char *buf;
- int err = 0;
+ snd_pcm_sframes_t ret = 0;
assert(pcm);
assert(pcm->p);
assert(pcm->stream);
- err = pulse_check_connection(pcm->p);
- if (err < 0)
+ ret = pulse_check_connection(pcm->p);
+ if (ret < 0)
goto finish;
/* Make sure the buffer pointer is in sync */
- update_ptr(pcm);
-
- assert(pcm->last_size >= (size * pcm->frame_size));
+ ret = update_ptr(pcm);
+ if (ret < 0)
+ goto finish;
buf =
(char *) areas->addr + (areas->first +
areas->step * offset) / 8;
- pa_stream_write(pcm->stream, buf, size * pcm->frame_size, NULL, 0,
- 0);
+ ret = pa_stream_write(pcm->stream, buf, size * pcm->frame_size, NULL, 0, 0);
+ if (ret < 0) {
+ ret = -EIO;
+ goto finish;
+ }
/* Make sure the buffer pointer is in sync */
- update_ptr(pcm);
+ ret = update_ptr(pcm);
+ if (ret < 0)
+ goto finish;
- if (pcm->last_size < pcm->buffer_attr.minreq)
- pulse_poll_deactivate(pcm->p);
- err = size;
+ ret = update_active(pcm);
+ if (ret < 0)
+ goto finish;
+
+ ret = size;
pcm->underrun = 0;
- finish:
+finish:
pa_threaded_mainloop_unlock(pcm->p->mainloop);
- return err;
+ return ret;
}
static snd_pcm_sframes_t pulse_read(snd_pcm_ioplug_t * io,
snd_pcm_uframes_t size)
{
snd_pcm_pulse_t *pcm = io->private_data;
- void *dst_buf, *src_buf;
+ void *dst_buf;
size_t remain_size, frag_length;
- int err = 0;
+ snd_pcm_sframes_t ret = 0;
assert(pcm);
assert(pcm->p);
assert(pcm->stream);
- err = pulse_check_connection(pcm->p);
- if (err < 0)
+ ret = pulse_check_connection(pcm->p);
+ if (ret < 0)
goto finish;
/* Make sure the buffer pointer is in sync */
- update_ptr(pcm);
+ ret = update_ptr(pcm);
+ if (ret < 0)
+ goto finish;
remain_size = size * pcm->frame_size;
(char *) areas->addr + (areas->first +
areas->step * offset) / 8;
while (remain_size > 0) {
- pa_stream_peek(pcm->stream, (const void **) &src_buf,
- &frag_length);
+ const void *src_buf;
+
+ if (pa_stream_peek(pcm->stream, &src_buf, &frag_length) < 0) {
+ ret = -EIO;
+ goto finish;
+ }
+
if (frag_length == 0)
break;
}
/* Make sure the buffer pointer is in sync */
- update_ptr(pcm);
+ ret = update_ptr(pcm);
+ if (ret < 0)
+ goto finish;
- if (pcm->last_size < pcm->buffer_attr.minreq)
- pulse_poll_deactivate(pcm->p);
+ ret = update_active(pcm);
+ if (ret < 0)
+ goto finish;
- err = size - (remain_size / pcm->frame_size);
+ ret = size - (remain_size / pcm->frame_size);
- finish:
+finish:
pa_threaded_mainloop_unlock(pcm->p->mainloop);
- return err;
+ return ret;
}
static void stream_request_cb(pa_stream * p, size_t length, void *userdata)
assert(pcm);
assert(pcm->p);
- pulse_poll_activate(pcm->p);
+ update_active(pcm);
}
static void stream_underrun_cb(pa_stream * p, void *userdata)
pcm->underrun = 1;
}
+static void stream_latency_cb(pa_stream *p, void *userdata) {
+ snd_pcm_pulse_t *pcm = userdata;
+
+ assert(pcm);
+ assert(pcm->p);
+
+ pa_threaded_mainloop_signal(pcm->p->mainloop, 0);
+}
+
static int pulse_pcm_poll_revents(snd_pcm_ioplug_t * io,
struct pollfd *pfd, unsigned int nfds,
unsigned short *revents)
{
- snd_pcm_pulse_t *pcm = io->private_data;
int err = 0;
+ snd_pcm_pulse_t *pcm = io->private_data;
assert(pcm);
assert(pcm->p);
pa_threaded_mainloop_lock(pcm->p->mainloop);
- *revents = 0;
+ err = check_active(pcm);
- /*
- * Make sure we have an up-to-date value.
- */
- update_ptr(pcm);
+ if (err < 0)
+ goto finish;
- /*
- * ALSA thinks in periods, not bytes, samples or frames.
- */
- if (pcm->last_size >= pcm->buffer_attr.minreq) {
- if (io->stream == SND_PCM_STREAM_PLAYBACK)
- *revents |= POLLOUT;
- else
- *revents |= POLLIN;
- }
+ if (err > 0)
+ *revents = io->stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
+ else
+ *revents = 0;
+
+finish:
pa_threaded_mainloop_unlock(pcm->p->mainloop);
pa_stream_set_state_callback(pcm->stream, pulse_stream_state_cb,
pcm->p);
+ pa_stream_set_latency_update_callback(pcm->stream, stream_latency_cb, pcm);
+
if (io->stream == SND_PCM_STREAM_PLAYBACK) {
pa_stream_set_write_callback(pcm->stream,
stream_request_cb, pcm);
goto finish;
}
- pcm->last_size = 0;
- pcm->ptr = 0;
pcm->offset = 0;
pcm->underrun = 0;
snd_pcm_hw_params_t * params)
{
snd_pcm_pulse_t *pcm = io->private_data;
- snd_pcm_t *base = io->pcm;
int err = 0;
assert(pcm);
if (pcm->stream) {
pa_stream_disconnect(pcm->stream);
- pulse_wait_stream_state(pcm->p, pcm->stream,
- PA_STREAM_TERMINATED);
pa_stream_unref(pcm->stream);
}