mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-12-23 07:06:31 +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;
|
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,
|
static int write_frames(struct libasound_state *state,
|
||||||
unsigned int *frame_count, unsigned int avail_count,
|
unsigned int *frame_count, unsigned int avail_count,
|
||||||
struct mapper_context *mapper,
|
struct mapper_context *mapper,
|
||||||
|
@ -231,6 +276,48 @@ error:
|
||||||
return err;
|
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)
|
static int irq_rw_pre_process(struct libasound_state *state)
|
||||||
{
|
{
|
||||||
struct rw_closure *closure = state->private_data;
|
struct rw_closure *closure = state->private_data;
|
||||||
|
@ -267,10 +354,17 @@ static int irq_rw_pre_process(struct libasound_state *state)
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (snd_pcm_stream(state->handle) == SND_PCM_STREAM_CAPTURE)
|
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;
|
closure->process_frames = r_process_frames_blocking;
|
||||||
|
} else {
|
||||||
|
if (state->nonblock)
|
||||||
|
closure->process_frames = w_process_frames_nonblocking;
|
||||||
else
|
else
|
||||||
closure->process_frames = w_process_frames_blocking;
|
closure->process_frames = w_process_frames_blocking;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,9 +14,10 @@ enum no_short_opts {
|
||||||
OPT_FATAL_ERRORS = 200,
|
OPT_FATAL_ERRORS = 200,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define S_OPTS "D:"
|
#define S_OPTS "D:N"
|
||||||
static const struct option l_opts[] = {
|
static const struct option l_opts[] = {
|
||||||
{"device", 1, 0, 'D'},
|
{"device", 1, 0, 'D'},
|
||||||
|
{"nonblock", 0, 0, 'N'},
|
||||||
// For debugging.
|
// For debugging.
|
||||||
{"fatal-errors", 0, 0, OPT_FATAL_ERRORS},
|
{"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')
|
if (key == 'D')
|
||||||
state->node_literal = arg_duplicate_string(optarg, &err);
|
state->node_literal = arg_duplicate_string(optarg, &err);
|
||||||
|
else if (key == 'N')
|
||||||
|
state->nonblock = true;
|
||||||
else if (key == OPT_FATAL_ERRORS)
|
else if (key == OPT_FATAL_ERRORS)
|
||||||
state->finish_at_xrun = true;
|
state->finish_at_xrun = true;
|
||||||
else
|
else
|
||||||
|
@ -91,10 +94,14 @@ static int set_access_hw_param(struct libasound_state *state)
|
||||||
static int open_handle(struct xfer_context *xfer)
|
static int open_handle(struct xfer_context *xfer)
|
||||||
{
|
{
|
||||||
struct libasound_state *state = xfer->private_data;
|
struct libasound_state *state = xfer->private_data;
|
||||||
|
int mode = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
if (state->nonblock)
|
||||||
|
mode |= SND_PCM_NONBLOCK;
|
||||||
|
|
||||||
err = snd_pcm_open(&state->handle, state->node_literal, xfer->direction,
|
err = snd_pcm_open(&state->handle, state->node_literal, xfer->direction,
|
||||||
0);
|
mode);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
logging(state, "Fail to open libasound PCM node for %s: %s\n",
|
logging(state, "Fail to open libasound PCM node for %s: %s\n",
|
||||||
snd_pcm_stream_name(xfer->direction),
|
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",
|
logging(state, "snd_pcm_drop(): %s\n",
|
||||||
snd_strerror(err));
|
snd_strerror(err));
|
||||||
} else {
|
} else {
|
||||||
|
// TODO: this is a bug in kernel land.
|
||||||
|
if (state->nonblock)
|
||||||
|
snd_pcm_nonblock(state->handle, 0);
|
||||||
err = snd_pcm_drain(state->handle);
|
err = snd_pcm_drain(state->handle);
|
||||||
|
if (state->nonblock)
|
||||||
|
snd_pcm_nonblock(state->handle, 1);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
logging(state, "snd_pcm_drain(): %s\n",
|
logging(state, "snd_pcm_drain(): %s\n",
|
||||||
snd_strerror(err));
|
snd_strerror(err));
|
||||||
|
|
|
@ -31,6 +31,7 @@ struct libasound_state {
|
||||||
char *node_literal;
|
char *node_literal;
|
||||||
|
|
||||||
bool finish_at_xrun:1;
|
bool finish_at_xrun:1;
|
||||||
|
bool nonblock:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
// For internal use in 'libasound' module.
|
// For internal use in 'libasound' module.
|
||||||
|
|
Loading…
Reference in a new issue