]> git.alsa-project.org Git - alsa-utils.git/commitdiff
axfer: add support for non-blocking operation
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Tue, 13 Nov 2018 06:41:34 +0000 (15:41 +0900)
committerTakashi Iwai <tiwai@suse.de>
Tue, 13 Nov 2018 11:04:36 +0000 (12:04 +0100)
In alsa-lib PCM API, snd_pcm_read[i|n]() and snd_pcm_write[i|n] can be
used with non-blocking mode. This is available when SND_PCM_NONBLOCK is
used as 'mode' argument for a call of snd_pcm_open().

This commit adds support this type of operation. To reduce CPU usage, this
commit uses 'snd_pcm_wait()' to wait for event notification.

Below lines are examples to execute:
$ axfer transfer -N -P -d 2 -D hw:0,3 /dev/urandom -f dat -vvv
$ axfer transfer -N -C -d 2 -D hw:1,0 /dev/null -r 48000 -vvv

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
axfer/xfer-libasound-irq-rw.c
axfer/xfer-libasound.c
axfer/xfer-libasound.h

index 59634b46fa965639b059aa7e77edabdad196740d..f05ac4b0e968d09643865cf96a11b29d8d9dda77 100644 (file)
@@ -117,6 +117,51 @@ error:
        return err;
 }
 
+static int r_process_frames_nonblocking(struct libasound_state *state,
+                                       snd_pcm_state_t status,
+                                       unsigned int *frame_count,
+                                       struct mapper_context *mapper,
+                                       struct container_context *cntrs)
+{
+       snd_pcm_sframes_t avail;
+       snd_pcm_uframes_t avail_count;
+       int err = 0;
+
+       if (status != SND_PCM_STATE_RUNNING) {
+               err = snd_pcm_start(state->handle);
+               if (err < 0)
+                       goto error;
+       }
+
+       // Wait for hardware IRQ when no available space.
+       err = snd_pcm_wait(state->handle, -1);
+       if (err < 0)
+               goto error;
+
+       // Check available space on the buffer.
+       avail = snd_pcm_avail(state->handle);
+       if (avail < 0) {
+               err = avail;
+               goto error;
+       }
+       avail_count = (snd_pcm_uframes_t)avail;
+
+       if (avail_count == 0) {
+               // Let's go to a next iteration.
+               err = 0;
+               goto error;
+       }
+
+       err = read_frames(state, frame_count, avail_count, mapper, cntrs);
+       if (err < 0)
+               goto error;
+
+       return 0;
+error:
+       *frame_count = 0;
+       return err;
+}
+
 static int write_frames(struct libasound_state *state,
                        unsigned int *frame_count, unsigned int avail_count,
                        struct mapper_context *mapper,
@@ -231,6 +276,48 @@ error:
        return err;
 }
 
+static int w_process_frames_nonblocking(struct libasound_state *state,
+                                       snd_pcm_state_t status,
+                                       unsigned int *frame_count,
+                                       struct mapper_context *mapper,
+                                       struct container_context *cntrs)
+{
+       snd_pcm_sframes_t avail;
+       unsigned int avail_count;
+       int err;
+
+       // Wait for hardware IRQ when no left space.
+       err = snd_pcm_wait(state->handle, -1);
+       if (err < 0)
+               goto error;
+
+       // Check available space on the buffer.
+       avail = snd_pcm_avail(state->handle);
+       if (avail < 0) {
+               err = avail;
+               goto error;
+       }
+       avail_count = (unsigned int)avail;
+
+       if (avail_count == 0) {
+               // Let's go to a next iteration.
+               err = 0;
+               goto error;
+       }
+
+       err = write_frames(state, frame_count, avail_count, mapper, cntrs);
+       if (err < 0)
+               goto error;
+
+       // NOTE: The substream starts automatically when the accumulated number
+       // of queued data frame exceeds start_threshold.
+
+       return 0;
+error:
+       *frame_count = 0;
+       return err;
+}
+
 static int irq_rw_pre_process(struct libasound_state *state)
 {
        struct rw_closure *closure = state->private_data;
@@ -267,10 +354,17 @@ static int irq_rw_pre_process(struct libasound_state *state)
        if (err < 0)
                return err;
 
-       if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE)
-               closure->process_frames = r_process_frames_blocking;
-       else
-               closure->process_frames = w_process_frames_blocking;
+       if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE) {
+               if (state->nonblock)
+                       closure->process_frames = r_process_frames_nonblocking;
+               else
+                       closure->process_frames = r_process_frames_blocking;
+       } else {
+               if (state->nonblock)
+                       closure->process_frames = w_process_frames_nonblocking;
+               else
+                       closure->process_frames = w_process_frames_blocking;
+       }
 
        return 0;
 }
index 77c142e2c7729e66b239e31cbc14a7525ae2059b..cb26b692c80b744533b5f95708c1b6edee95302b 100644 (file)
@@ -14,9 +14,10 @@ enum no_short_opts {
        OPT_FATAL_ERRORS = 200,
 };
 
-#define S_OPTS "D:"
+#define S_OPTS "D:N"
 static const struct option l_opts[] = {
        {"device",              1, 0, 'D'},
+       {"nonblock",            0, 0, 'N'},
        // For debugging.
        {"fatal-errors",        0, 0, OPT_FATAL_ERRORS},
 };
@@ -46,6 +47,8 @@ static int xfer_libasound_parse_opt(struct xfer_context *xfer, int key,
 
        if (key == 'D')
                state->node_literal = arg_duplicate_string(optarg, &err);
+       else if (key == 'N')
+               state->nonblock = true;
        else if (key == OPT_FATAL_ERRORS)
                state->finish_at_xrun = true;
        else
@@ -91,10 +94,14 @@ static int set_access_hw_param(struct libasound_state *state)
 static int open_handle(struct xfer_context *xfer)
 {
        struct libasound_state *state = xfer->private_data;
+       int mode = 0;
        int err;
 
+       if (state->nonblock)
+               mode |= SND_PCM_NONBLOCK;
+
        err = snd_pcm_open(&state->handle, state->node_literal, xfer->direction,
-                          0);
+                          mode);
        if (err < 0) {
                logging(state, "Fail to open libasound PCM node for %s: %s\n",
                        snd_pcm_stream_name(xfer->direction),
@@ -378,7 +385,12 @@ static void xfer_libasound_post_process(struct xfer_context *xfer)
                                logging(state, "snd_pcm_drop(): %s\n",
                                       snd_strerror(err));
                } else {
+                       // TODO: this is a bug in kernel land.
+                       if (state->nonblock)
+                               snd_pcm_nonblock(state->handle, 0);
                        err = snd_pcm_drain(state->handle);
+                       if (state->nonblock)
+                               snd_pcm_nonblock(state->handle, 1);
                        if (err < 0)
                                logging(state, "snd_pcm_drain(): %s\n",
                                       snd_strerror(err));
index 270288d838a6b886ba29af82be361b2de55f44fb..6656aebcf3fd68e7127c6ac09857e9e81c9c470a 100644 (file)
@@ -31,6 +31,7 @@ struct libasound_state {
        char *node_literal;
 
        bool finish_at_xrun:1;
+       bool nonblock:1;
 };
 
 // For internal use in 'libasound' module.