[alsa-devel] [PATCH - JACK plugin 4/4] jack: Support snd_pcm_drain()
twischer at de.adit-jv.com
twischer at de.adit-jv.com
Thu Mar 1 14:14:08 CET 2018
From: Timo Wischer <twischer at de.adit-jv.com>
Block on drain till available samples played
Without this commit the JACK thread will be stopped
before the ALSA buffer was completely forwarded to
the JACK daemon.
Signed-off-by: Timo Wischer <twischer at de.adit-jv.com>
diff --git a/jack/pcm_jack.c b/jack/pcm_jack.c
index 79aa79b..f457dc4 100644
--- a/jack/pcm_jack.c
+++ b/jack/pcm_jack.c
@@ -28,6 +28,17 @@
#include <alsa/asoundlib.h>
#include <alsa/pcm_external.h>
+/* ALSA supports up to 64 periods per buffer.
+ * Therefore at least 64 retries are valid and
+ * should not be handled as an error case
+ */
+#define MAX_DRAIN_RETRIES 100
+/* ALSA supports a a period with 8192 frames.
+ * This would result in ~170ms at 48kHz.
+ * Therefore a time out of 1 second is sufficient
+ */
+#define DRAIN_TIMEOUT 1000
+
typedef enum _jack_format {
SND_PCM_JACK_FORMAT_RAW
} snd_pcm_jack_format_t;
@@ -146,7 +157,13 @@ static int pcm_poll_unblock_check(snd_pcm_ioplug_t *io)
snd_pcm_jack_t *jack = io->private_data;
avail = snd_pcm_avail_update(io->pcm);
- if (avail < 0 || avail >= jack->min_avail) {
+ /* In draining state poll_fd is used to wait
+ * till all pending frames are played.
+ * Therefore it has to be guarantee that a poll event is also generated
+ * if the buffer contains less than min_avail frames
+ */
+ if (avail < 0 || avail >= jack->min_avail ||
+ jack->state == SND_PCM_STATE_DRAINING) {
write(jack->fd, &buf, 1);
return 1;
}
@@ -232,7 +249,8 @@ snd_pcm_jack_process_cb(jack_nframes_t nframes, snd_pcm_ioplug_t *io)
}
hw_ptr = jack->hw_ptr;
- if (jack->state == SND_PCM_STATE_RUNNING) {
+ if (jack->state == SND_PCM_STATE_RUNNING ||
+ jack->state == SND_PCM_STATE_DRAINING) {
const snd_pcm_channel_area_t *areas = snd_pcm_ioplug_mmap_areas(io);
while (xfer < nframes) {
@@ -392,6 +410,61 @@ static int snd_pcm_jack_start(snd_pcm_ioplug_t *io)
return err;
}
+static int snd_pcm_jack_drain(snd_pcm_ioplug_t *io)
+{
+ snd_pcm_jack_t *jack = io->private_data;
+ const snd_pcm_state_t state = jack->state;
+ unsigned int retries = MAX_DRAIN_RETRIES;
+ char buf[32];
+
+ /* Immediately stop on capture device.
+ * snd_pcm_jack_stop() will be automatically called
+ * by snd_pcm_ioplug_drain()
+ */
+ if (io->stream == SND_PCM_STREAM_CAPTURE) {
+ return 0;
+ }
+
+ if (snd_pcm_jack_hw_avail(io, jack->hw_ptr, io->appl_ptr) <= 0) {
+ /* No data pending. Nothing to drain. */
+ return 0;
+ }
+
+ /* start device if not yet done */
+ if (state == SND_PCM_STATE_PREPARED) {
+ snd_pcm_jack_start(io);
+ }
+
+ /* FIXME: io->state will not be set to SND_PCM_STATE_DRAINING by the
+ * ALSA library before calling this function.
+ * Therefore this state has to be stored internally.
+ */
+ jack->state = SND_PCM_STATE_DRAINING;
+
+ struct pollfd pfd;
+ pfd.fd = io->poll_fd;
+ pfd.events = io->poll_events | POLLERR | POLLNVAL;
+
+ while (snd_pcm_jack_hw_avail(io, jack->hw_ptr, io->appl_ptr) > 0) {
+ if (retries <= 0) {
+ SNDERR("Pending frames not yet processed.");
+ return -ETIMEDOUT;
+ }
+
+ if (poll(&pfd, 1, DRAIN_TIMEOUT) < 0) {
+ SNDERR("Waiting for next JACK process callback failed (err %d)",
+ -errno);
+ return -errno;
+ }
+
+ /* clean pending events. */
+ while (read(io->poll_fd, &buf, sizeof(buf)) == sizeof(buf))
+ ;
+ }
+
+ return 0;
+}
+
static int snd_pcm_jack_stop(snd_pcm_ioplug_t *io)
{
snd_pcm_jack_t *jack = io->private_data;
@@ -423,6 +496,7 @@ static snd_pcm_ioplug_callback_t jack_pcm_callback = {
.stop = snd_pcm_jack_stop,
.pointer = snd_pcm_jack_pointer,
.prepare = snd_pcm_jack_prepare,
+ .drain = snd_pcm_jack_drain,
.poll_revents = snd_pcm_jack_poll_revents,
};
--
2.7.4
More information about the Alsa-devel
mailing list