mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-31 22:25:35 -04:00
Plugin updates. The action callback has a new argument.
The mmap plugin is updated to accept frags_min & frags_max. Also, SND_PCM_STOP_ROLLOVER behaves much better.
This commit is contained in:
parent
43041cbe78
commit
54d348c4d7
5 changed files with 137 additions and 34 deletions
|
|
@ -86,7 +86,9 @@ struct snd_stru_pcm_plugin {
|
||||||
char *dst_ptr, size_t dst_size);
|
char *dst_ptr, size_t dst_size);
|
||||||
ssize_t (*src_size)(snd_pcm_plugin_t *plugin, size_t dst_size);
|
ssize_t (*src_size)(snd_pcm_plugin_t *plugin, size_t dst_size);
|
||||||
ssize_t (*dst_size)(snd_pcm_plugin_t *plugin, size_t src_size);
|
ssize_t (*dst_size)(snd_pcm_plugin_t *plugin, size_t src_size);
|
||||||
int (*action)(snd_pcm_plugin_t *plugin, snd_pcm_plugin_action_t action);
|
int (*action)(snd_pcm_plugin_t *plugin,
|
||||||
|
snd_pcm_plugin_action_t action,
|
||||||
|
unsigned long data);
|
||||||
snd_pcm_plugin_t *prev;
|
snd_pcm_plugin_t *prev;
|
||||||
snd_pcm_plugin_t *next;
|
snd_pcm_plugin_t *next;
|
||||||
void *private_data;
|
void *private_data;
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,8 @@ int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int snd_pcm_plugin_action(snd_pcm_t *pcm, int channel, int action)
|
static int snd_pcm_plugin_action(snd_pcm_t *pcm, int channel, int action,
|
||||||
|
unsigned long data)
|
||||||
{
|
{
|
||||||
snd_pcm_plugin_t *plugin;
|
snd_pcm_plugin_t *plugin;
|
||||||
int err;
|
int err;
|
||||||
|
|
@ -208,7 +209,7 @@ static int snd_pcm_plugin_action(snd_pcm_t *pcm, int channel, int action)
|
||||||
plugin = pcm->plugin_first[channel];
|
plugin = pcm->plugin_first[channel];
|
||||||
while (plugin) {
|
while (plugin) {
|
||||||
if (plugin->action) {
|
if (plugin->action) {
|
||||||
if ((err = plugin->action(plugin, action))<0)
|
if ((err = plugin->action(plugin, action, data))<0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
plugin = plugin->next;
|
plugin = plugin->next;
|
||||||
|
|
@ -295,7 +296,7 @@ int snd_pcm_plugin_params(snd_pcm_t *pcm, snd_pcm_channel_params_t *params)
|
||||||
err = snd_pcm_channel_params(pcm, &hwparams);
|
err = snd_pcm_channel_params(pcm, &hwparams);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
err = snd_pcm_plugin_action(pcm, hwparams.channel, INIT);
|
err = snd_pcm_plugin_action(pcm, hwparams.channel, INIT, (long)&hwparams);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -347,7 +348,7 @@ int snd_pcm_plugin_prepare(snd_pcm_t *pcm, int channel)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if ((err = snd_pcm_plugin_action(pcm, channel, PREPARE))<0)
|
if ((err = snd_pcm_plugin_action(pcm, channel, PREPARE, 0))<0)
|
||||||
return err;
|
return err;
|
||||||
return snd_pcm_channel_prepare(pcm, channel);
|
return snd_pcm_channel_prepare(pcm, channel);
|
||||||
}
|
}
|
||||||
|
|
@ -356,7 +357,7 @@ int snd_pcm_plugin_playback_drain(snd_pcm_t *pcm)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if ((err = snd_pcm_plugin_action(pcm, SND_PCM_CHANNEL_PLAYBACK, DRAIN))<0)
|
if ((err = snd_pcm_plugin_action(pcm, SND_PCM_CHANNEL_PLAYBACK, DRAIN, 0))<0)
|
||||||
return err;
|
return err;
|
||||||
return snd_pcm_playback_drain(pcm);
|
return snd_pcm_playback_drain(pcm);
|
||||||
}
|
}
|
||||||
|
|
@ -366,7 +367,7 @@ int snd_pcm_plugin_flush(snd_pcm_t *pcm, int channel)
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
pdprintf("flush\n");
|
pdprintf("flush\n");
|
||||||
if ((err = snd_pcm_plugin_action(pcm, channel, FLUSH))<0)
|
if ((err = snd_pcm_plugin_action(pcm, channel, FLUSH, 0))<0)
|
||||||
return err;
|
return err;
|
||||||
return snd_pcm_channel_flush(pcm, channel);
|
return snd_pcm_channel_flush(pcm, channel);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -509,7 +509,9 @@ static ssize_t adpcm_transfer(snd_pcm_plugin_t * plugin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adpcm_action(snd_pcm_plugin_t * plugin, snd_pcm_plugin_action_t action)
|
static int adpcm_action(snd_pcm_plugin_t * plugin,
|
||||||
|
snd_pcm_plugin_action_t action,
|
||||||
|
unsigned long udata)
|
||||||
{
|
{
|
||||||
struct adpcm_private_data *data;
|
struct adpcm_private_data *data;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,34 @@ struct mmap_private_data {
|
||||||
snd_pcm_mmap_control_t *control;
|
snd_pcm_mmap_control_t *control;
|
||||||
char *buffer;
|
char *buffer;
|
||||||
int frag;
|
int frag;
|
||||||
|
int start_mode, stop_mode;
|
||||||
|
int frags, frags_used;
|
||||||
|
int frags_min, frags_max;
|
||||||
|
unsigned int lastblock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int playback_ok(struct mmap_private_data *data)
|
||||||
|
{
|
||||||
|
snd_pcm_mmap_control_t *control = data->control;
|
||||||
|
int delta = control->status.block;
|
||||||
|
|
||||||
|
if (delta < data->lastblock) {
|
||||||
|
delta += (~0 - data->lastblock) + 1;
|
||||||
|
} else {
|
||||||
|
delta -= data->lastblock;
|
||||||
|
}
|
||||||
|
data->frags_used -= delta;
|
||||||
|
if (data->frags_used < 0) {
|
||||||
|
/* correction for rollover */
|
||||||
|
data->frag += -data->frags_used;
|
||||||
|
data->frag %= data->frags;
|
||||||
|
data->frags_used = 0;
|
||||||
|
}
|
||||||
|
data->lastblock += delta;
|
||||||
|
return data->frags_used <= data->frags_max &&
|
||||||
|
(data->frags - data->frags_used) >= data->frags_min;
|
||||||
|
}
|
||||||
|
|
||||||
static int poll_playback(snd_pcm_t *pcm)
|
static int poll_playback(snd_pcm_t *pcm)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
@ -53,17 +79,23 @@ static int poll_playback(snd_pcm_t *pcm)
|
||||||
return err < 0 ? err : 0;
|
return err < 0 ? err : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int query_playback(struct mmap_private_data *data,
|
static int query_playback(struct mmap_private_data *data, int not_use_poll)
|
||||||
snd_pcm_mmap_control_t *control,
|
|
||||||
int not_use_poll)
|
|
||||||
{
|
{
|
||||||
|
snd_pcm_mmap_control_t *control = data->control;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
switch (control->status.status) {
|
switch (control->status.status) {
|
||||||
case SND_PCM_STATUS_PREPARED:
|
case SND_PCM_STATUS_PREPARED:
|
||||||
|
if (data->start_mode == SND_PCM_START_GO)
|
||||||
|
return -EAGAIN;
|
||||||
|
if ((data->start_mode == SND_PCM_START_DATA &&
|
||||||
|
playback_ok(data)) ||
|
||||||
|
(data->start_mode == SND_PCM_START_FULL &&
|
||||||
|
data->frags_used == data->frags)) {
|
||||||
err = snd_pcm_channel_go(data->pcm, data->channel);
|
err = snd_pcm_channel_go(data->pcm, data->channel);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case SND_PCM_STATUS_RUNNING:
|
case SND_PCM_STATUS_RUNNING:
|
||||||
if (!not_use_poll) {
|
if (!not_use_poll) {
|
||||||
|
|
@ -81,6 +113,27 @@ static int query_playback(struct mmap_private_data *data,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int capture_ok(struct mmap_private_data *data)
|
||||||
|
{
|
||||||
|
snd_pcm_mmap_control_t *control = data->control;
|
||||||
|
int delta = control->status.block;
|
||||||
|
|
||||||
|
if (delta < data->lastblock) {
|
||||||
|
delta += (~0 - data->lastblock) + 1;
|
||||||
|
} else {
|
||||||
|
delta -= data->lastblock;
|
||||||
|
}
|
||||||
|
data->frags_used += delta;
|
||||||
|
if (data->frags_used > data->frags) {
|
||||||
|
/* correction for rollover */
|
||||||
|
data->frag += data->frags_used - data->frags;
|
||||||
|
data->frag %= data->frags;
|
||||||
|
data->frags_used = data->frags;
|
||||||
|
}
|
||||||
|
data->lastblock += delta;
|
||||||
|
return data->frags_used >= data->frags_min;
|
||||||
|
}
|
||||||
|
|
||||||
static int poll_capture(snd_pcm_t *pcm)
|
static int poll_capture(snd_pcm_t *pcm)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
@ -95,21 +148,22 @@ static int poll_capture(snd_pcm_t *pcm)
|
||||||
return err < 0 ? err : 0;
|
return err < 0 ? err : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int query_capture(struct mmap_private_data *data,
|
static int query_capture(struct mmap_private_data *data, int not_use_poll)
|
||||||
snd_pcm_mmap_control_t *control,
|
|
||||||
int not_use_poll)
|
|
||||||
{
|
{
|
||||||
|
snd_pcm_mmap_control_t *control = data->control;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
switch (control->status.status) {
|
switch (control->status.status) {
|
||||||
case SND_PCM_STATUS_PREPARED:
|
case SND_PCM_STATUS_PREPARED:
|
||||||
|
if (data->start_mode != SND_PCM_START_DATA)
|
||||||
|
return -EAGAIN;
|
||||||
err = snd_pcm_channel_go(data->pcm, data->channel);
|
err = snd_pcm_channel_go(data->pcm, data->channel);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
break;
|
break;
|
||||||
case SND_PCM_STATUS_RUNNING:
|
case SND_PCM_STATUS_RUNNING:
|
||||||
if (!not_use_poll) {
|
if (!not_use_poll) {
|
||||||
control->status.expblock = control->status.block + 1;
|
control->status.expblock = control->status.block + data->frags_min;
|
||||||
err = poll_capture(data->pcm);
|
err = poll_capture(data->pcm);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
|
|
@ -142,8 +196,8 @@ static int mmap_transfer_src_ptr(snd_pcm_plugin_t *plugin, char **buffer, size_t
|
||||||
*buffer = data->buffer + control->fragments[data->frag].addr;
|
*buffer = data->buffer + control->fragments[data->frag].addr;
|
||||||
if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
|
if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
|
||||||
/* wait until the block is not free */
|
/* wait until the block is not free */
|
||||||
while (control->fragments[data->frag].data) {
|
while (!playback_ok(data)) {
|
||||||
err = query_playback(data, control, 0);
|
err = query_playback(data, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -181,8 +235,8 @@ static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
|
if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
|
||||||
while (control->fragments[data->frag].data) {
|
while (!playback_ok(data)) {
|
||||||
err = query_playback(data, control, 0);
|
err = query_playback(data, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -192,13 +246,14 @@ static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
|
||||||
memcpy(addr, dst_ptr, dst_size);
|
memcpy(addr, dst_ptr, dst_size);
|
||||||
control->fragments[data->frag++].data = 1;
|
control->fragments[data->frag++].data = 1;
|
||||||
data->frag %= control->status.frags;
|
data->frag %= control->status.frags;
|
||||||
|
data->frags_used++;
|
||||||
} else {
|
} else {
|
||||||
int frag;
|
int frag;
|
||||||
|
|
||||||
for (voice = 0; voice < control->status.voices; voice++) {
|
for (voice = 0; voice < control->status.voices; voice++) {
|
||||||
frag = data->frag + (voice * (control->status.frags / control->status.voices));
|
frag = data->frag + (voice * data->frags);
|
||||||
while (control->fragments[frag].data) {
|
while (control->fragments[frag].data) {
|
||||||
err = query_playback(data, control, 1);
|
err = query_playback(data, 1);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -209,12 +264,13 @@ static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
|
||||||
dst_ptr += control->status.frag_size;
|
dst_ptr += control->status.frag_size;
|
||||||
}
|
}
|
||||||
data->frag++;
|
data->frag++;
|
||||||
data->frag %= control->status.frags;
|
data->frag %= data->frags;
|
||||||
|
data->frags_used++;
|
||||||
}
|
}
|
||||||
return dst_size;
|
return dst_size;
|
||||||
} else if (data->channel == SND_PCM_CHANNEL_CAPTURE) {
|
} else if (data->channel == SND_PCM_CHANNEL_CAPTURE) {
|
||||||
while (!control->fragments[data->frag].data) {
|
while (!capture_ok(data)) {
|
||||||
err = query_capture(data, control, 0);
|
err = query_capture(data, 0);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -224,20 +280,26 @@ static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
|
||||||
memcpy(dst_ptr, addr, dst_size);
|
memcpy(dst_ptr, addr, dst_size);
|
||||||
control->fragments[data->frag++].data = 0;
|
control->fragments[data->frag++].data = 0;
|
||||||
data->frag %= control->status.frags;
|
data->frag %= control->status.frags;
|
||||||
|
data->frags_used--;
|
||||||
} else {
|
} else {
|
||||||
|
int frag;
|
||||||
|
|
||||||
for (voice = 0; voice < control->status.voices; voice++) {
|
for (voice = 0; voice < control->status.voices; voice++) {
|
||||||
|
frag = data->frag + (voice * data->frags);
|
||||||
while (!control->fragments[data->frag].data) {
|
while (!control->fragments[data->frag].data) {
|
||||||
err = query_capture(data, control, 1);
|
err = query_capture(data, 1);
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
addr = data->buffer + control->fragments[data->frag].addr;
|
addr = data->buffer + control->fragments[frag].addr;
|
||||||
if (dst_ptr != addr)
|
if (dst_ptr != addr)
|
||||||
memcpy(dst_ptr, addr, control->status.frag_size);
|
memcpy(dst_ptr, addr, control->status.frag_size);
|
||||||
control->fragments[data->frag++].data = 0;
|
control->fragments[frag].data = 0;
|
||||||
data->frag %= control->status.frags;
|
|
||||||
dst_ptr += control->status.frag_size;
|
dst_ptr += control->status.frag_size;
|
||||||
}
|
}
|
||||||
|
data->frag++;
|
||||||
|
data->frag %= data->frags;
|
||||||
|
data->frags_used--;
|
||||||
}
|
}
|
||||||
return dst_size;
|
return dst_size;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -245,7 +307,9 @@ static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mmap_action(snd_pcm_plugin_t *plugin, snd_pcm_plugin_action_t action)
|
static int mmap_action(snd_pcm_plugin_t *plugin,
|
||||||
|
snd_pcm_plugin_action_t action,
|
||||||
|
unsigned long udata)
|
||||||
{
|
{
|
||||||
struct mmap_private_data *data;
|
struct mmap_private_data *data;
|
||||||
|
|
||||||
|
|
@ -253,15 +317,47 @@ static int mmap_action(snd_pcm_plugin_t *plugin, snd_pcm_plugin_action_t action)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
data = (struct mmap_private_data *)snd_pcm_plugin_extra_data(plugin);
|
data = (struct mmap_private_data *)snd_pcm_plugin_extra_data(plugin);
|
||||||
if (action == INIT) {
|
if (action == INIT) {
|
||||||
|
snd_pcm_channel_params_t *params;
|
||||||
|
snd_pcm_channel_setup_t setup;
|
||||||
|
int result, frags;
|
||||||
|
|
||||||
if (data->control)
|
if (data->control)
|
||||||
snd_pcm_munmap(data->pcm, data->channel);
|
snd_pcm_munmap(data->pcm, data->channel);
|
||||||
return snd_pcm_mmap(data->pcm, data->channel, &data->control, (void **)&data->buffer);
|
result = snd_pcm_mmap(data->pcm, data->channel, &data->control, (void **)&data->buffer);
|
||||||
|
if (result < 0)
|
||||||
|
return result;
|
||||||
|
params = (snd_pcm_channel_params_t *)udata;
|
||||||
|
data->start_mode = params->start_mode;
|
||||||
|
data->stop_mode = params->stop_mode;
|
||||||
|
memset(&setup, 0, sizeof(setup));
|
||||||
|
setup.channel = data->channel;
|
||||||
|
if ((result = snd_pcm_channel_setup(data->pcm, &setup)) < 0)
|
||||||
|
return result;
|
||||||
|
data->frags = setup.buf.block.frags;
|
||||||
|
data->frags_min = setup.buf.block.frags_min;
|
||||||
|
data->frags_max = setup.buf.block.frags_max;
|
||||||
|
if (data->frags_min < 0)
|
||||||
|
data->frags_min = 0;
|
||||||
|
if (data->frags_min >= setup.buf.block.frags)
|
||||||
|
data->frags_min = setup.buf.block.frags - 1;
|
||||||
|
if (data->frags_max < 0)
|
||||||
|
data->frags_max = setup.buf.block.frags + data->frags_max;
|
||||||
|
if (data->frags_max < data->frags_min)
|
||||||
|
data->frags_max = data->frags_min;
|
||||||
|
if (data->frags_max < 1)
|
||||||
|
data->frags_max = 1;
|
||||||
|
if (data->frags_max > setup.buf.block.frags)
|
||||||
|
data->frags_max = setup.buf.block.frags;
|
||||||
|
return 0;
|
||||||
} else if (action == PREPARE) {
|
} else if (action == PREPARE) {
|
||||||
data->frag = 0;
|
data->frag = 0;
|
||||||
|
data->lastblock = 0;
|
||||||
} else if (action == DRAIN && data->channel == SND_PCM_CHANNEL_PLAYBACK) {
|
} else if (action == DRAIN && data->channel == SND_PCM_CHANNEL_PLAYBACK) {
|
||||||
data->frag = 0;
|
data->frag = 0;
|
||||||
|
data->lastblock = 0;
|
||||||
} else if (action == FLUSH) {
|
} else if (action == FLUSH) {
|
||||||
data->frag = 0;
|
data->frag = 0;
|
||||||
|
data->lastblock = 0;
|
||||||
}
|
}
|
||||||
return 0; /* silenty ignore other actions */
|
return 0; /* silenty ignore other actions */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -336,7 +336,9 @@ static ssize_t rate_transfer(snd_pcm_plugin_t *plugin,
|
||||||
return rate_dst_size(plugin, src_size);
|
return rate_dst_size(plugin, src_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rate_action(snd_pcm_plugin_t *plugin, snd_pcm_plugin_action_t action)
|
static int rate_action(snd_pcm_plugin_t *plugin,
|
||||||
|
snd_pcm_plugin_action_t action,
|
||||||
|
unsigned long udata)
|
||||||
{
|
{
|
||||||
struct rate_private_data *data;
|
struct rate_private_data *data;
|
||||||
int voice;
|
int voice;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue