mirror of
https://github.com/alsa-project/alsa-lib.git
synced 2025-10-29 05:40:25 -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);
|
||||
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);
|
||||
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 *next;
|
||||
void *private_data;
|
||||
|
|
|
|||
|
|
@ -200,7 +200,8 @@ int snd_pcm_plugin_info(snd_pcm_t *pcm, snd_pcm_channel_info_t *info)
|
|||
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;
|
||||
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];
|
||||
while (plugin) {
|
||||
if (plugin->action) {
|
||||
if ((err = plugin->action(plugin, action))<0)
|
||||
if ((err = plugin->action(plugin, action, data))<0)
|
||||
return err;
|
||||
}
|
||||
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);
|
||||
if (err < 0)
|
||||
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)
|
||||
return err;
|
||||
return 0;
|
||||
|
|
@ -347,7 +348,7 @@ int snd_pcm_plugin_prepare(snd_pcm_t *pcm, int channel)
|
|||
{
|
||||
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 snd_pcm_channel_prepare(pcm, channel);
|
||||
}
|
||||
|
|
@ -356,7 +357,7 @@ int snd_pcm_plugin_playback_drain(snd_pcm_t *pcm)
|
|||
{
|
||||
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 snd_pcm_playback_drain(pcm);
|
||||
}
|
||||
|
|
@ -366,7 +367,7 @@ int snd_pcm_plugin_flush(snd_pcm_t *pcm, int channel)
|
|||
int err;
|
||||
|
||||
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 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -37,8 +37,34 @@ struct mmap_private_data {
|
|||
snd_pcm_mmap_control_t *control;
|
||||
char *buffer;
|
||||
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)
|
||||
{
|
||||
int err;
|
||||
|
|
@ -53,17 +79,23 @@ static int poll_playback(snd_pcm_t *pcm)
|
|||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static int query_playback(struct mmap_private_data *data,
|
||||
snd_pcm_mmap_control_t *control,
|
||||
int not_use_poll)
|
||||
static int query_playback(struct mmap_private_data *data, int not_use_poll)
|
||||
{
|
||||
snd_pcm_mmap_control_t *control = data->control;
|
||||
int err;
|
||||
|
||||
switch (control->status.status) {
|
||||
case SND_PCM_STATUS_PREPARED:
|
||||
err = snd_pcm_channel_go(data->pcm, data->channel);
|
||||
if (err < 0)
|
||||
return err;
|
||||
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);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
break;
|
||||
case SND_PCM_STATUS_RUNNING:
|
||||
if (!not_use_poll) {
|
||||
|
|
@ -81,6 +113,27 @@ static int query_playback(struct mmap_private_data *data,
|
|||
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)
|
||||
{
|
||||
int err;
|
||||
|
|
@ -95,21 +148,22 @@ static int poll_capture(snd_pcm_t *pcm)
|
|||
return err < 0 ? err : 0;
|
||||
}
|
||||
|
||||
static int query_capture(struct mmap_private_data *data,
|
||||
snd_pcm_mmap_control_t *control,
|
||||
int not_use_poll)
|
||||
static int query_capture(struct mmap_private_data *data, int not_use_poll)
|
||||
{
|
||||
snd_pcm_mmap_control_t *control = data->control;
|
||||
int err;
|
||||
|
||||
switch (control->status.status) {
|
||||
case SND_PCM_STATUS_PREPARED:
|
||||
if (data->start_mode != SND_PCM_START_DATA)
|
||||
return -EAGAIN;
|
||||
err = snd_pcm_channel_go(data->pcm, data->channel);
|
||||
if (err < 0)
|
||||
return err;
|
||||
break;
|
||||
case SND_PCM_STATUS_RUNNING:
|
||||
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);
|
||||
if (err < 0)
|
||||
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;
|
||||
if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
|
||||
/* wait until the block is not free */
|
||||
while (control->fragments[data->frag].data) {
|
||||
err = query_playback(data, control, 0);
|
||||
while (!playback_ok(data)) {
|
||||
err = query_playback(data, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
|
@ -181,8 +235,8 @@ static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
|
|||
return -EINVAL;
|
||||
}
|
||||
if (data->channel == SND_PCM_CHANNEL_PLAYBACK) {
|
||||
while (control->fragments[data->frag].data) {
|
||||
err = query_playback(data, control, 0);
|
||||
while (!playback_ok(data)) {
|
||||
err = query_playback(data, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
|
@ -192,13 +246,14 @@ static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
|
|||
memcpy(addr, dst_ptr, dst_size);
|
||||
control->fragments[data->frag++].data = 1;
|
||||
data->frag %= control->status.frags;
|
||||
data->frags_used++;
|
||||
} else {
|
||||
int frag;
|
||||
|
||||
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) {
|
||||
err = query_playback(data, control, 1);
|
||||
err = query_playback(data, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
|
@ -209,12 +264,13 @@ static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
|
|||
dst_ptr += control->status.frag_size;
|
||||
}
|
||||
data->frag++;
|
||||
data->frag %= control->status.frags;
|
||||
data->frag %= data->frags;
|
||||
data->frags_used++;
|
||||
}
|
||||
return dst_size;
|
||||
} else if (data->channel == SND_PCM_CHANNEL_CAPTURE) {
|
||||
while (!control->fragments[data->frag].data) {
|
||||
err = query_capture(data, control, 0);
|
||||
while (!capture_ok(data)) {
|
||||
err = query_capture(data, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
|
@ -224,20 +280,26 @@ static ssize_t mmap_transfer(snd_pcm_plugin_t *plugin,
|
|||
memcpy(dst_ptr, addr, dst_size);
|
||||
control->fragments[data->frag++].data = 0;
|
||||
data->frag %= control->status.frags;
|
||||
data->frags_used--;
|
||||
} else {
|
||||
int frag;
|
||||
|
||||
for (voice = 0; voice < control->status.voices; voice++) {
|
||||
frag = data->frag + (voice * data->frags);
|
||||
while (!control->fragments[data->frag].data) {
|
||||
err = query_capture(data, control, 1);
|
||||
err = query_capture(data, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
addr = data->buffer + control->fragments[data->frag].addr;
|
||||
addr = data->buffer + control->fragments[frag].addr;
|
||||
if (dst_ptr != addr)
|
||||
memcpy(dst_ptr, addr, control->status.frag_size);
|
||||
control->fragments[data->frag++].data = 0;
|
||||
data->frag %= control->status.frags;
|
||||
control->fragments[frag].data = 0;
|
||||
dst_ptr += control->status.frag_size;
|
||||
}
|
||||
data->frag++;
|
||||
data->frag %= data->frags;
|
||||
data->frags_used--;
|
||||
}
|
||||
return dst_size;
|
||||
} 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;
|
||||
|
||||
|
|
@ -253,15 +317,47 @@ static int mmap_action(snd_pcm_plugin_t *plugin, snd_pcm_plugin_action_t action)
|
|||
return -EINVAL;
|
||||
data = (struct mmap_private_data *)snd_pcm_plugin_extra_data(plugin);
|
||||
if (action == INIT) {
|
||||
snd_pcm_channel_params_t *params;
|
||||
snd_pcm_channel_setup_t setup;
|
||||
int result, frags;
|
||||
|
||||
if (data->control)
|
||||
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) {
|
||||
data->frag = 0;
|
||||
data->lastblock = 0;
|
||||
} else if (action == DRAIN && data->channel == SND_PCM_CHANNEL_PLAYBACK) {
|
||||
data->frag = 0;
|
||||
data->lastblock = 0;
|
||||
} else if (action == FLUSH) {
|
||||
data->frag = 0;
|
||||
data->lastblock = 0;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
int voice;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue