mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-10 06:15:43 +01:00
axfer: add support for non-blocking operation
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>
This commit is contained in:
parent
29c6076029
commit
c46d46a436
3 changed files with 113 additions and 6 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -31,6 +31,7 @@ struct libasound_state {
|
|||
char *node_literal;
|
||||
|
||||
bool finish_at_xrun:1;
|
||||
bool nonblock:1;
|
||||
};
|
||||
|
||||
// For internal use in 'libasound' module.
|
||||
|
|
Loading…
Reference in a new issue