1998-08-13 15:42:56 +00:00
|
|
|
/*
|
|
|
|
|
* PCM Interface - main file
|
1999-05-11 22:15:16 +00:00
|
|
|
* Copyright (c) 1998 by Jaroslav Kysela <perex@suse.cz>
|
2000-05-08 18:53:38 +00:00
|
|
|
* Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
|
1998-08-27 20:47:51 +00:00
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU Library General Public License as
|
|
|
|
|
* published by the Free Software Foundation; either version 2 of
|
|
|
|
|
* the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU Library General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Library General Public
|
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
*
|
1998-08-13 15:42:56 +00:00
|
|
|
*/
|
1999-06-02 00:40:30 +00:00
|
|
|
|
1998-08-13 15:42:56 +00:00
|
|
|
#include <stdio.h>
|
2000-05-26 17:58:24 +00:00
|
|
|
#include <string.h>
|
2000-05-08 18:53:38 +00:00
|
|
|
#include <malloc.h>
|
1998-08-13 15:42:56 +00:00
|
|
|
#include <errno.h>
|
2000-10-11 12:37:27 +00:00
|
|
|
#include <stdarg.h>
|
2000-07-04 19:29:16 +00:00
|
|
|
#include <sys/ioctl.h>
|
2000-05-08 18:53:38 +00:00
|
|
|
#include <sys/poll.h>
|
2000-10-20 09:18:13 +00:00
|
|
|
#include <sys/shm.h>
|
|
|
|
|
#include <sys/mman.h>
|
2000-11-20 20:10:46 +00:00
|
|
|
#include <limits.h>
|
2000-09-24 09:57:26 +00:00
|
|
|
#include <dlfcn.h>
|
1999-11-06 23:47:07 +00:00
|
|
|
#include "pcm_local.h"
|
2000-08-24 17:07:44 +00:00
|
|
|
#include "list.h"
|
1998-08-13 15:42:56 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
snd_pcm_type_t snd_pcm_type(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
assert(pcm);
|
|
|
|
|
return pcm->type;
|
2000-01-08 20:11:33 +00:00
|
|
|
}
|
|
|
|
|
|
2000-11-20 20:10:46 +00:00
|
|
|
snd_pcm_type_t snd_pcm_stream(snd_pcm_t *pcm)
|
2000-01-08 20:11:33 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
|
|
|
|
return pcm->stream;
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
2000-01-08 20:11:33 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_close(snd_pcm_t *pcm)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
int err;
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
if (pcm->setup) {
|
2000-10-15 14:15:30 +00:00
|
|
|
if (pcm->mode & SND_PCM_NONBLOCK)
|
|
|
|
|
snd_pcm_drop(pcm);
|
|
|
|
|
else
|
|
|
|
|
snd_pcm_drain(pcm);
|
|
|
|
|
}
|
2000-11-20 20:10:46 +00:00
|
|
|
if (pcm->mmap_channels) {
|
2000-10-14 10:31:34 +00:00
|
|
|
if ((err = snd_pcm_munmap(pcm)) < 0)
|
2000-05-08 18:53:38 +00:00
|
|
|
ret = err;
|
1999-08-22 13:34:31 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
if ((err = pcm->ops->close(pcm->op_arg)) < 0)
|
2000-05-08 18:53:38 +00:00
|
|
|
ret = err;
|
2000-11-20 20:10:46 +00:00
|
|
|
pcm->setup = 0;
|
2000-10-10 09:11:07 +00:00
|
|
|
if (pcm->name)
|
|
|
|
|
free(pcm->name);
|
2000-09-24 09:57:26 +00:00
|
|
|
free(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
return 0;
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
1998-08-13 15:42:56 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_nonblock(snd_pcm_t *pcm, int nonblock)
|
1998-08-13 15:42:56 +00:00
|
|
|
{
|
2000-05-08 18:53:38 +00:00
|
|
|
int err;
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-10-11 12:37:27 +00:00
|
|
|
if ((err = pcm->ops->nonblock(pcm->op_arg, nonblock)) < 0)
|
2000-05-08 18:53:38 +00:00
|
|
|
return err;
|
|
|
|
|
if (nonblock)
|
2000-09-24 09:57:26 +00:00
|
|
|
pcm->mode |= SND_PCM_NONBLOCK;
|
2000-05-08 18:53:38 +00:00
|
|
|
else
|
2000-09-24 09:57:26 +00:00
|
|
|
pcm->mode &= ~SND_PCM_NONBLOCK;
|
1999-11-07 16:43:13 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-10-11 12:37:27 +00:00
|
|
|
int snd_pcm_async(snd_pcm_t *pcm, int sig, pid_t pid)
|
|
|
|
|
{
|
|
|
|
|
assert(pcm);
|
|
|
|
|
return pcm->ops->async(pcm->op_arg, sig, pid);
|
|
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_info(snd_pcm_t *pcm, snd_pcm_info_t *info)
|
1998-08-13 15:42:56 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm && info);
|
|
|
|
|
return pcm->ops->info(pcm->op_arg, info);
|
1998-08-13 15:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
2000-11-20 20:10:46 +00:00
|
|
|
int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_hw_info_t *info)
|
1998-08-13 15:42:56 +00:00
|
|
|
{
|
1999-11-06 23:47:07 +00:00
|
|
|
int err;
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm && info);
|
|
|
|
|
#if 0
|
|
|
|
|
fprintf(stderr, "hw_info entered:\n");
|
|
|
|
|
snd_pcm_dump_hw_info(info, stderr);
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
#endif
|
|
|
|
|
err = pcm->ops->hw_info(pcm->op_arg, info);
|
|
|
|
|
#if 0
|
|
|
|
|
fprintf(stderr, "hw_info return %d:\n", err);
|
|
|
|
|
snd_pcm_dump_hw_info(info, stderr);
|
|
|
|
|
fprintf(stderr, "\n");
|
|
|
|
|
#endif
|
|
|
|
|
return err;
|
1998-08-13 15:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
void snd_pcm_hw_info_any(snd_pcm_hw_info_t *info)
|
2000-11-25 21:34:36 +00:00
|
|
|
{
|
|
|
|
|
assert(info);
|
|
|
|
|
info->access_mask = ~0;
|
|
|
|
|
info->format_mask = ~0;
|
|
|
|
|
info->subformat_mask = ~0;
|
|
|
|
|
info->channels_min = 1;
|
|
|
|
|
info->channels_max = UINT_MAX;
|
|
|
|
|
info->rate_min = 1;
|
|
|
|
|
info->rate_max = UINT_MAX;
|
2000-12-07 15:58:03 +00:00
|
|
|
info->fragment_length_min = 0;
|
|
|
|
|
info->fragment_length_max = UINT_MAX;
|
2000-11-25 21:34:36 +00:00
|
|
|
info->fragments_min = 1;
|
|
|
|
|
info->fragments_max = UINT_MAX;
|
2000-12-07 15:58:03 +00:00
|
|
|
info->buffer_length_min = 1;
|
|
|
|
|
info->buffer_length_max = UINT_MAX;
|
2000-11-25 21:34:36 +00:00
|
|
|
}
|
|
|
|
|
|
2000-11-20 20:10:46 +00:00
|
|
|
void snd_pcm_hw_params_to_info(snd_pcm_hw_params_t *params, snd_pcm_hw_info_t *info)
|
2000-08-28 09:14:37 +00:00
|
|
|
{
|
2000-11-25 21:34:36 +00:00
|
|
|
assert(info && params);
|
2000-11-20 20:10:46 +00:00
|
|
|
info->access_mask = 1U << params->access;
|
|
|
|
|
info->format_mask = 1U << params->format;
|
|
|
|
|
info->subformat_mask = 1U << params->subformat;
|
|
|
|
|
info->channels_min = info->channels_max = params->channels;
|
|
|
|
|
info->rate_min = info->rate_max = params->rate;
|
2000-12-07 15:58:03 +00:00
|
|
|
info->fragment_length_min = info->fragment_length_max = muldiv_down(params->fragment_size, 1000000, params->rate);
|
2000-11-20 20:10:46 +00:00
|
|
|
info->fragments_min = info->fragments_max = params->fragments;
|
2000-12-07 15:58:03 +00:00
|
|
|
info->buffer_length_min = info->buffer_length_max = muldiv_down(params->fragment_size * params->fragments, 1000000, params->rate);
|
2000-08-28 09:14:37 +00:00
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
int snd_pcm_hw_info_to_params(snd_pcm_t *pcm, snd_pcm_hw_info_t *info, snd_pcm_hw_params_t *params)
|
1998-08-13 15:42:56 +00:00
|
|
|
{
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_t i = *info;
|
2000-06-21 14:59:20 +00:00
|
|
|
int err;
|
2000-11-20 20:10:46 +00:00
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
err = snd_pcm_hw_info(pcm, &i);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2000-11-20 20:10:46 +00:00
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
assert(i.access_mask);
|
|
|
|
|
if (i.access_mask & (i.access_mask - 1)) {
|
|
|
|
|
i.access_mask = 1 << (ffs(i.access_mask - 1));
|
|
|
|
|
err = snd_pcm_hw_info(pcm, info);
|
|
|
|
|
assert(err >= 0);
|
|
|
|
|
}
|
|
|
|
|
assert(i.format_mask);
|
|
|
|
|
if (i.format_mask & (i.format_mask - 1)) {
|
|
|
|
|
i.format_mask = 1 << (ffs(i.format_mask - 1));
|
|
|
|
|
err = snd_pcm_hw_info(pcm, info);
|
|
|
|
|
assert(err >= 0);
|
|
|
|
|
}
|
|
|
|
|
assert(i.subformat_mask);
|
|
|
|
|
if (i.subformat_mask & (i.subformat_mask - 1)) {
|
|
|
|
|
i.subformat_mask = 1 << (ffs(i.subformat_mask - 1));
|
|
|
|
|
err = snd_pcm_hw_info(pcm, info);
|
|
|
|
|
assert(err >= 0);
|
|
|
|
|
}
|
|
|
|
|
assert(i.channels_min <= i.channels_max);
|
|
|
|
|
if (i.channels_min < i.channels_max) {
|
|
|
|
|
i.channels_max = i.channels_min;
|
|
|
|
|
err = snd_pcm_hw_info(pcm, info);
|
|
|
|
|
assert(err >= 0);
|
|
|
|
|
}
|
|
|
|
|
assert(i.rate_min <= i.rate_max);
|
|
|
|
|
if (i.rate_min < i.rate_max) {
|
|
|
|
|
i.rate_max = i.rate_min;
|
|
|
|
|
err = snd_pcm_hw_info(pcm, info);
|
|
|
|
|
assert(err >= 0);
|
|
|
|
|
}
|
2000-12-07 15:58:03 +00:00
|
|
|
assert(i.fragment_length_min <= i.fragment_length_max);
|
|
|
|
|
if (i.fragment_length_min < i.fragment_length_max) {
|
|
|
|
|
i.fragment_length_max = i.fragment_length_min;
|
2000-11-29 08:32:36 +00:00
|
|
|
err = snd_pcm_hw_info(pcm, info);
|
|
|
|
|
assert(err >= 0);
|
|
|
|
|
}
|
|
|
|
|
assert(i.fragments_min <= i.fragments_max);
|
|
|
|
|
if (i.fragments_min < i.fragments_max) {
|
|
|
|
|
i.fragments_max = i.fragments_min;
|
|
|
|
|
err = snd_pcm_hw_info(pcm, info);
|
|
|
|
|
assert(err >= 0);
|
|
|
|
|
}
|
|
|
|
|
params->access = ffs(i.access_mask) - 1;
|
|
|
|
|
params->format = ffs(i.format_mask) - 1;
|
|
|
|
|
params->subformat = ffs(i.subformat_mask) - 1;
|
|
|
|
|
params->channels = i.channels_min;
|
|
|
|
|
params->rate = i.rate_min;
|
2000-12-07 15:58:03 +00:00
|
|
|
params->fragment_size = muldiv_near(i.fragment_length_min, i.rate_min, 1000000);
|
2000-11-29 08:32:36 +00:00
|
|
|
params->fragments = i.fragments_min;
|
2000-11-20 20:10:46 +00:00
|
|
|
return 0;
|
2000-10-20 09:18:13 +00:00
|
|
|
}
|
|
|
|
|
|
2000-11-20 20:10:46 +00:00
|
|
|
int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
|
2000-10-20 09:18:13 +00:00
|
|
|
{
|
2000-11-20 20:10:46 +00:00
|
|
|
int err;
|
2000-10-20 09:18:13 +00:00
|
|
|
assert(pcm && params);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
|
|
|
|
if ((err = pcm->ops->sw_params(pcm->op_arg, params)) < 0)
|
|
|
|
|
return err;
|
|
|
|
|
pcm->start_mode = params->start_mode;
|
|
|
|
|
pcm->ready_mode = params->ready_mode;
|
|
|
|
|
pcm->xrun_mode = params->xrun_mode;
|
|
|
|
|
pcm->avail_min = params->avail_min;
|
|
|
|
|
pcm->xfer_min = params->xfer_min;
|
|
|
|
|
pcm->xfer_align = params->xfer_align;
|
|
|
|
|
pcm->time = params->time;
|
|
|
|
|
pcm->boundary = params->boundary;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_dig_params(snd_pcm_t *pcm, snd_pcm_dig_params_t *params)
|
|
|
|
|
{
|
|
|
|
|
assert(pcm && params);
|
|
|
|
|
return pcm->ops->dig_params(pcm->op_arg, params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_dig_info(snd_pcm_t *pcm, snd_pcm_dig_info_t *info)
|
|
|
|
|
{
|
|
|
|
|
assert(pcm && info);
|
|
|
|
|
return pcm->ops->dig_info(pcm->op_arg, info);
|
2000-10-20 09:18:13 +00:00
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_status(snd_pcm_t *pcm, snd_pcm_status_t *status)
|
1998-08-13 15:42:56 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm && status);
|
|
|
|
|
return pcm->fast_ops->status(pcm->fast_op_arg, status);
|
1998-08-13 15:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_state(snd_pcm_t *pcm)
|
1998-08-13 15:42:56 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
|
|
|
|
return pcm->fast_ops->state(pcm->fast_op_arg);
|
1999-11-06 23:47:07 +00:00
|
|
|
}
|
1998-11-27 14:57:39 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_delay(snd_pcm_t *pcm, ssize_t *delayp)
|
1999-11-06 23:47:07 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-09-24 09:57:26 +00:00
|
|
|
return pcm->fast_ops->delay(pcm->fast_op_arg, delayp);
|
1998-08-13 15:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_prepare(snd_pcm_t *pcm)
|
1998-08-13 15:42:56 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-09-24 09:57:26 +00:00
|
|
|
return pcm->fast_ops->prepare(pcm->fast_op_arg);
|
1998-08-13 15:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
2000-11-24 17:08:03 +00:00
|
|
|
int snd_pcm_reset(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
assert(pcm);
|
|
|
|
|
assert(pcm->setup);
|
|
|
|
|
return pcm->fast_ops->reset(pcm->fast_op_arg);
|
|
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_start(snd_pcm_t *pcm)
|
1998-08-13 15:42:56 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-09-24 09:57:26 +00:00
|
|
|
return pcm->fast_ops->start(pcm->fast_op_arg);
|
1998-08-13 15:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
2000-10-02 06:59:59 +00:00
|
|
|
int snd_pcm_drop(snd_pcm_t *pcm)
|
1998-08-25 15:38:50 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-10-02 06:59:59 +00:00
|
|
|
return pcm->fast_ops->drop(pcm->fast_op_arg);
|
1998-08-13 15:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
2000-09-29 20:49:18 +00:00
|
|
|
int snd_pcm_drain(snd_pcm_t *pcm)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-09-29 20:49:18 +00:00
|
|
|
return pcm->fast_ops->drain(pcm->fast_op_arg);
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_pause(snd_pcm_t *pcm, int enable)
|
1998-08-13 15:42:56 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-09-24 09:57:26 +00:00
|
|
|
return pcm->fast_ops->pause(pcm->fast_op_arg, enable);
|
1998-08-13 15:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
2000-05-08 18:53:38 +00:00
|
|
|
|
2000-09-26 09:46:05 +00:00
|
|
|
ssize_t snd_pcm_rewind(snd_pcm_t *pcm, size_t frames)
|
2000-05-29 19:53:30 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-09-26 09:46:05 +00:00
|
|
|
assert(frames > 0);
|
|
|
|
|
return pcm->fast_ops->rewind(pcm->fast_op_arg, frames);
|
2000-05-29 19:53:30 +00:00
|
|
|
}
|
|
|
|
|
|
2000-10-16 11:34:11 +00:00
|
|
|
int snd_pcm_set_avail_min(snd_pcm_t *pcm, size_t frames)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-10-20 09:18:13 +00:00
|
|
|
assert(frames > 0);
|
2000-10-16 11:34:11 +00:00
|
|
|
err = pcm->fast_ops->set_avail_min(pcm->fast_op_arg, frames);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2000-11-20 20:10:46 +00:00
|
|
|
pcm->avail_min = frames;
|
2000-10-16 11:34:11 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
ssize_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, size_t size)
|
1998-08-13 15:42:56 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-06-04 13:13:01 +00:00
|
|
|
assert(size == 0 || buffer);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
|
|
|
|
assert(pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED);
|
2000-09-29 13:50:42 +00:00
|
|
|
return _snd_pcm_writei(pcm, buffer, size);
|
1998-08-13 15:42:56 +00:00
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
ssize_t snd_pcm_writen(snd_pcm_t *pcm, void **bufs, size_t size)
|
2000-01-31 12:40:05 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
|
|
|
|
assert(size == 0 || bufs);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
|
|
|
|
assert(pcm->access == SND_PCM_ACCESS_RW_NONINTERLEAVED);
|
2000-09-29 13:50:42 +00:00
|
|
|
return _snd_pcm_writen(pcm, bufs, size);
|
2000-01-31 12:40:05 +00:00
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
ssize_t snd_pcm_readi(snd_pcm_t *pcm, void *buffer, size_t size)
|
1998-08-13 15:42:56 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-06-04 13:13:01 +00:00
|
|
|
assert(size == 0 || buffer);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
|
|
|
|
assert(pcm->access == SND_PCM_ACCESS_RW_INTERLEAVED);
|
2000-09-29 13:50:42 +00:00
|
|
|
return _snd_pcm_readi(pcm, buffer, size);
|
1998-08-13 15:42:56 +00:00
|
|
|
}
|
1999-11-06 23:47:07 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
ssize_t snd_pcm_readn(snd_pcm_t *pcm, void **bufs, size_t size)
|
2000-01-31 12:40:05 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
|
|
|
|
assert(size == 0 || bufs);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
|
|
|
|
assert(pcm->access == SND_PCM_ACCESS_RW_NONINTERLEAVED);
|
2000-09-29 13:50:42 +00:00
|
|
|
return _snd_pcm_readn(pcm, bufs, size);
|
2000-05-08 18:53:38 +00:00
|
|
|
}
|
2000-01-31 12:40:05 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
ssize_t snd_pcm_writev(snd_pcm_t *pcm, const struct iovec *vector, int count)
|
2000-07-24 08:19:34 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
void **bufs;
|
|
|
|
|
int k;
|
|
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
|
|
|
|
assert((int)pcm->channels == count);
|
2000-09-24 09:57:26 +00:00
|
|
|
bufs = alloca(sizeof(*bufs) * count);
|
|
|
|
|
for (k = 0; k < count; ++k) {
|
|
|
|
|
bufs[k] = vector[k].iov_base;
|
|
|
|
|
assert(vector[k].iov_len == vector[0].iov_len);
|
2000-07-24 08:19:34 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
return snd_pcm_writen(pcm, bufs, vector[0].iov_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ssize_t snd_pcm_readv(snd_pcm_t *pcm, const struct iovec *vector, int count)
|
|
|
|
|
{
|
|
|
|
|
void **bufs;
|
|
|
|
|
int k;
|
|
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
|
|
|
|
assert((int)pcm->channels == count);
|
2000-09-24 09:57:26 +00:00
|
|
|
bufs = alloca(sizeof(*bufs) * count);
|
|
|
|
|
for (k = 0; k < count; ++k) {
|
|
|
|
|
bufs[k] = vector[k].iov_base;
|
|
|
|
|
assert(vector[k].iov_len == vector[0].iov_len);
|
2000-07-24 08:19:34 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
return snd_pcm_readn(pcm, bufs, vector[0].iov_len);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* FIXME */
|
|
|
|
|
#define snd_pcm_link_descriptor snd_pcm_poll_descriptor
|
|
|
|
|
|
|
|
|
|
int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
|
|
|
|
|
{
|
|
|
|
|
int fd1 = snd_pcm_link_descriptor(pcm1);
|
|
|
|
|
int fd2 = snd_pcm_link_descriptor(pcm2);
|
|
|
|
|
if (fd1 < 0 || fd2 < 0)
|
|
|
|
|
return -ENOSYS;
|
2000-10-14 19:43:14 +00:00
|
|
|
if (ioctl(fd1, SND_PCM_IOCTL_LINK, fd2) < 0) {
|
|
|
|
|
SYSERR("SND_PCM_IOCTL_LINK failed");
|
2000-07-24 08:19:34 +00:00
|
|
|
return -errno;
|
2000-10-14 19:43:14 +00:00
|
|
|
}
|
2000-07-24 08:19:34 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_unlink(snd_pcm_t *pcm)
|
2000-07-24 08:19:34 +00:00
|
|
|
{
|
|
|
|
|
int fd;
|
2000-09-24 09:57:26 +00:00
|
|
|
switch (pcm->type) {
|
2000-07-24 08:19:34 +00:00
|
|
|
case SND_PCM_TYPE_HW:
|
|
|
|
|
case SND_PCM_TYPE_MULTI:
|
2000-09-24 09:57:26 +00:00
|
|
|
fd = snd_pcm_poll_descriptor(pcm);
|
2000-07-24 08:19:34 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2000-10-14 19:43:14 +00:00
|
|
|
return -ENOSYS;
|
2000-07-24 08:19:34 +00:00
|
|
|
}
|
2000-10-14 19:43:14 +00:00
|
|
|
if (ioctl(fd, SND_PCM_IOCTL_UNLINK) < 0) {
|
|
|
|
|
SYSERR("SND_PCM_IOCTL_UNLINK failed");
|
2000-07-24 08:19:34 +00:00
|
|
|
return -errno;
|
2000-10-14 19:43:14 +00:00
|
|
|
}
|
2000-07-24 08:19:34 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_poll_descriptor(snd_pcm_t *pcm)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-10-14 10:31:34 +00:00
|
|
|
return pcm->poll_fd;
|
2000-01-31 12:40:05 +00:00
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
#define STATE(v) [SND_PCM_STATE_##v] = #v
|
|
|
|
|
#define STREAM(v) [SND_PCM_STREAM_##v] = #v
|
|
|
|
|
#define READY(v) [SND_PCM_READY_##v] = #v
|
|
|
|
|
#define XRUN(v) [SND_PCM_XRUN_##v] = #v
|
|
|
|
|
#define ACCESS(v) [SND_PCM_ACCESS_##v] = #v
|
|
|
|
|
#define START(v) [SND_PCM_START_##v] = #v
|
2000-12-07 15:58:03 +00:00
|
|
|
#define HW_INFO(v) [SND_PCM_HW_INFO_##v] = #v
|
2000-11-29 08:32:36 +00:00
|
|
|
#define HW_PARAM(v) [SND_PCM_HW_PARAM_##v] = #v
|
|
|
|
|
#define SW_PARAM(v) [SND_PCM_SW_PARAM_##v] = #v
|
|
|
|
|
#define FORMAT(v) [SND_PCM_FORMAT_##v] = #v
|
|
|
|
|
#define SUBFORMAT(v) [SND_PCM_SUBFORMAT_##v] = #v
|
2000-05-25 08:36:58 +00:00
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
#define FORMATD(v, d) [SND_PCM_FORMAT_##v] = d
|
|
|
|
|
#define SUBFORMATD(v, d) [SND_PCM_SUBFORMAT_##v] = d
|
2000-05-25 08:36:58 +00:00
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
char *snd_pcm_stream_names[] = {
|
2000-11-20 20:10:46 +00:00
|
|
|
STREAM(PLAYBACK),
|
|
|
|
|
STREAM(CAPTURE),
|
|
|
|
|
};
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
char *snd_pcm_state_names[] = {
|
2000-11-20 20:10:46 +00:00
|
|
|
STATE(OPEN),
|
|
|
|
|
STATE(SETUP),
|
|
|
|
|
STATE(PREPARED),
|
|
|
|
|
STATE(RUNNING),
|
|
|
|
|
STATE(XRUN),
|
|
|
|
|
STATE(PAUSED),
|
|
|
|
|
};
|
|
|
|
|
|
2000-12-07 15:58:03 +00:00
|
|
|
char *snd_pcm_hw_info_names[] = {
|
|
|
|
|
HW_INFO(ACCESS),
|
|
|
|
|
HW_INFO(FORMAT),
|
|
|
|
|
HW_INFO(SUBFORMAT),
|
|
|
|
|
HW_INFO(CHANNELS),
|
|
|
|
|
HW_INFO(RATE),
|
|
|
|
|
HW_INFO(FRAGMENT_LENGTH),
|
|
|
|
|
HW_INFO(FRAGMENTS),
|
|
|
|
|
HW_INFO(BUFFER_LENGTH),
|
|
|
|
|
};
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
char *snd_pcm_hw_param_names[] = {
|
2000-11-20 20:10:46 +00:00
|
|
|
HW_PARAM(ACCESS),
|
|
|
|
|
HW_PARAM(FORMAT),
|
|
|
|
|
HW_PARAM(SUBFORMAT),
|
|
|
|
|
HW_PARAM(CHANNELS),
|
|
|
|
|
HW_PARAM(RATE),
|
|
|
|
|
HW_PARAM(FRAGMENT_SIZE),
|
|
|
|
|
HW_PARAM(FRAGMENTS),
|
|
|
|
|
};
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
char *snd_pcm_sw_param_names[] = {
|
2000-11-20 20:10:46 +00:00
|
|
|
SW_PARAM(START_MODE),
|
|
|
|
|
SW_PARAM(READY_MODE),
|
|
|
|
|
SW_PARAM(AVAIL_MIN),
|
|
|
|
|
SW_PARAM(XFER_MIN),
|
|
|
|
|
SW_PARAM(XFER_ALIGN),
|
|
|
|
|
SW_PARAM(XRUN_MODE),
|
|
|
|
|
SW_PARAM(TIME),
|
|
|
|
|
};
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
char *snd_pcm_access_names[] = {
|
2000-11-20 20:10:46 +00:00
|
|
|
ACCESS(MMAP_INTERLEAVED),
|
|
|
|
|
ACCESS(MMAP_NONINTERLEAVED),
|
|
|
|
|
ACCESS(MMAP_COMPLEX),
|
|
|
|
|
ACCESS(RW_INTERLEAVED),
|
|
|
|
|
ACCESS(RW_NONINTERLEAVED),
|
|
|
|
|
};
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
char *snd_pcm_format_names[] = {
|
|
|
|
|
FORMAT(S8),
|
|
|
|
|
FORMAT(U8),
|
|
|
|
|
FORMAT(S16_LE),
|
|
|
|
|
FORMAT(S16_BE),
|
|
|
|
|
FORMAT(U16_LE),
|
|
|
|
|
FORMAT(U16_BE),
|
|
|
|
|
FORMAT(S24_LE),
|
|
|
|
|
FORMAT(S24_BE),
|
|
|
|
|
FORMAT(U24_LE),
|
|
|
|
|
FORMAT(U24_BE),
|
|
|
|
|
FORMAT(S32_LE),
|
|
|
|
|
FORMAT(S32_BE),
|
|
|
|
|
FORMAT(U32_LE),
|
|
|
|
|
FORMAT(U32_BE),
|
|
|
|
|
FORMAT(FLOAT_LE),
|
|
|
|
|
FORMAT(FLOAT_BE),
|
|
|
|
|
FORMAT(FLOAT64_LE),
|
|
|
|
|
FORMAT(FLOAT64_BE),
|
|
|
|
|
FORMAT(IEC958_SUBFRAME_LE),
|
|
|
|
|
FORMAT(IEC958_SUBFRAME_BE),
|
|
|
|
|
FORMAT(MU_LAW),
|
|
|
|
|
FORMAT(A_LAW),
|
|
|
|
|
FORMAT(IMA_ADPCM),
|
|
|
|
|
FORMAT(MPEG),
|
|
|
|
|
FORMAT(GSM),
|
|
|
|
|
FORMAT(SPECIAL),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
char *snd_pcm_format_descriptions[] = {
|
|
|
|
|
FORMATD(S8, "Signed 8-bit"),
|
|
|
|
|
FORMATD(U8, "Unsigned 8-bit"),
|
|
|
|
|
FORMATD(S16_LE, "Signed 16-bit Little Endian"),
|
|
|
|
|
FORMATD(S16_BE, "Signed 16-bit Big Endian"),
|
|
|
|
|
FORMATD(U16_LE, "Unsigned 16-bit Little Endian"),
|
|
|
|
|
FORMATD(U16_BE, "Unsigned 16-bit Big Endian"),
|
|
|
|
|
FORMATD(S24_LE, "Signed 24-bit Little Endian"),
|
|
|
|
|
FORMATD(S24_BE, "Signed 24-bit Big Endian"),
|
|
|
|
|
FORMATD(U24_LE, "Unsigned 24-bit Little Endian"),
|
|
|
|
|
FORMATD(U24_BE, "Unsigned 24-bit Big Endian"),
|
|
|
|
|
FORMATD(S32_LE, "Signed 32-bit Little Endian"),
|
|
|
|
|
FORMATD(S32_BE, "Signed 32-bit Big Endian"),
|
|
|
|
|
FORMATD(U32_LE, "Unsigned 32-bit Little Endian"),
|
|
|
|
|
FORMATD(U32_BE, "Unsigned 32-bit Big Endian"),
|
|
|
|
|
FORMATD(FLOAT_LE, "Float Little Endian"),
|
|
|
|
|
FORMATD(FLOAT_BE, "Float Big Endian"),
|
|
|
|
|
FORMATD(FLOAT64_LE, "Float64 Little Endian"),
|
|
|
|
|
FORMATD(FLOAT64_BE, "Float64 Big Endian"),
|
|
|
|
|
FORMATD(IEC958_SUBFRAME_LE, "IEC-958 Little Endian"),
|
|
|
|
|
FORMATD(IEC958_SUBFRAME_BE, "IEC-958 Big Endian"),
|
|
|
|
|
FORMATD(MU_LAW, "Mu-Law"),
|
|
|
|
|
FORMATD(A_LAW, "A-Law"),
|
|
|
|
|
FORMATD(IMA_ADPCM, "Ima-ADPCM"),
|
|
|
|
|
FORMATD(MPEG, "MPEG"),
|
|
|
|
|
FORMATD(GSM, "GSM"),
|
|
|
|
|
FORMATD(SPECIAL, "Special"),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
char *snd_pcm_subformat_names[] = {
|
|
|
|
|
SUBFORMAT(STD),
|
2000-05-25 08:36:58 +00:00
|
|
|
};
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
char *snd_pcm_subformat_descriptions[] = {
|
|
|
|
|
SUBFORMATD(STD, "Standard"),
|
2000-11-20 20:10:46 +00:00
|
|
|
};
|
2000-05-25 08:36:58 +00:00
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
char *snd_pcm_start_mode_names[] = {
|
2000-11-20 20:10:46 +00:00
|
|
|
START(EXPLICIT),
|
|
|
|
|
START(DATA),
|
|
|
|
|
};
|
2000-11-29 08:32:36 +00:00
|
|
|
|
|
|
|
|
char *snd_pcm_ready_mode_names[] = {
|
2000-11-20 20:10:46 +00:00
|
|
|
READY(FRAGMENT),
|
|
|
|
|
READY(ASAP),
|
|
|
|
|
};
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
char *snd_pcm_xrun_mode_names[] = {
|
2000-11-20 20:10:46 +00:00
|
|
|
XRUN(ASAP),
|
|
|
|
|
XRUN(FRAGMENT),
|
|
|
|
|
XRUN(NONE),
|
|
|
|
|
};
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
static char *onoff[] = {
|
|
|
|
|
[0] = "OFF",
|
|
|
|
|
[1] = "ON",
|
2000-11-20 20:10:46 +00:00
|
|
|
};
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
#define assoc(value, names) ({ \
|
|
|
|
|
unsigned int __v = value; \
|
|
|
|
|
assert(__v < sizeof(names) / sizeof(names[0])); \
|
|
|
|
|
names[__v]; \
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
2000-11-20 20:10:46 +00:00
|
|
|
int snd_pcm_dump_hw_setup(snd_pcm_t *pcm, FILE *fp)
|
|
|
|
|
{
|
|
|
|
|
assert(pcm);
|
|
|
|
|
assert(fp);
|
|
|
|
|
assert(pcm->setup);
|
2000-11-29 08:32:36 +00:00
|
|
|
fprintf(fp, "stream : %s\n", assoc(pcm->stream, snd_pcm_stream_names));
|
|
|
|
|
fprintf(fp, "access : %s\n", assoc(pcm->access, snd_pcm_access_names));
|
|
|
|
|
fprintf(fp, "format : %s\n", assoc(pcm->format, snd_pcm_format_names));
|
|
|
|
|
fprintf(fp, "subformat : %s\n", assoc(pcm->subformat, snd_pcm_subformat_names));
|
2000-11-20 20:10:46 +00:00
|
|
|
fprintf(fp, "channels : %d\n", pcm->channels);
|
|
|
|
|
fprintf(fp, "rate : %d\n", pcm->rate);
|
2000-12-04 10:07:51 +00:00
|
|
|
fprintf(fp, "exact rate : %g (%d/%d)\n", (double) pcm->rate_num / pcm->rate_den, pcm->rate_num, pcm->rate_den);
|
2000-11-20 20:10:46 +00:00
|
|
|
fprintf(fp, "msbits : %d\n", pcm->msbits);
|
|
|
|
|
fprintf(fp, "fragment_size: %ld\n", (long)pcm->fragment_size);
|
|
|
|
|
fprintf(fp, "fragments : %d\n", pcm->fragments);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_dump_sw_setup(snd_pcm_t *pcm, FILE *fp)
|
2000-05-25 08:36:58 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-07-17 15:33:29 +00:00
|
|
|
assert(fp);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-11-29 08:32:36 +00:00
|
|
|
fprintf(fp, "start_mode : %s\n", assoc(pcm->start_mode, snd_pcm_start_mode_names));
|
|
|
|
|
fprintf(fp, "ready_mode : %s\n", assoc(pcm->ready_mode, snd_pcm_ready_mode_names));
|
|
|
|
|
fprintf(fp, "xrun_mode : %s\n", assoc(pcm->xrun_mode, snd_pcm_xrun_mode_names));
|
2000-11-20 20:10:46 +00:00
|
|
|
fprintf(fp, "avail_min : %ld\n", (long)pcm->avail_min);
|
|
|
|
|
fprintf(fp, "xfer_min : %ld\n", (long)pcm->xfer_min);
|
|
|
|
|
fprintf(fp, "xfer_align : %ld\n", (long)pcm->xfer_align);
|
|
|
|
|
fprintf(fp, "time : %s\n", assoc(pcm->time, onoff));
|
|
|
|
|
fprintf(fp, "boundary : %ld\n", (long)pcm->boundary);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_dump_setup(snd_pcm_t *pcm, FILE *fp)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_dump_hw_setup(pcm, fp);
|
|
|
|
|
snd_pcm_dump_sw_setup(pcm, fp);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_dump_hw_params_fail(snd_pcm_hw_params_t *params, FILE *fp)
|
|
|
|
|
{
|
|
|
|
|
int k;
|
2000-11-29 08:32:36 +00:00
|
|
|
if (params->fail_mask == 0) {
|
|
|
|
|
fprintf(fp, "unknown hw_params failure reason\n");
|
2000-11-20 20:10:46 +00:00
|
|
|
return 0;
|
2000-11-29 08:32:36 +00:00
|
|
|
}
|
2000-11-20 20:10:46 +00:00
|
|
|
fprintf(fp, "hw_params failed on the following field value(s):\n");
|
|
|
|
|
for (k = 0; k <= SND_PCM_HW_PARAM_LAST; ++k) {
|
|
|
|
|
if (!(params->fail_mask & (1U << k)))
|
|
|
|
|
continue;
|
|
|
|
|
switch (k) {
|
|
|
|
|
case SND_PCM_HW_PARAM_ACCESS:
|
2000-11-29 08:32:36 +00:00
|
|
|
fprintf(fp, "access: %s\n", assoc(params->access, snd_pcm_access_names));
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
|
|
|
|
case SND_PCM_HW_PARAM_FORMAT:
|
2000-11-29 08:32:36 +00:00
|
|
|
fprintf(fp, "format: %s\n", assoc(params->format, snd_pcm_format_names));
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
|
|
|
|
case SND_PCM_HW_PARAM_SUBFORMAT:
|
2000-11-29 08:32:36 +00:00
|
|
|
fprintf(fp, "subformat: %s\n", assoc(params->subformat, snd_pcm_subformat_names));
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
|
|
|
|
case SND_PCM_HW_PARAM_CHANNELS:
|
|
|
|
|
fprintf(fp, "channels: %d\n", params->channels);
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_HW_PARAM_RATE:
|
|
|
|
|
fprintf(fp, "rate: %d\n", params->rate);
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_HW_PARAM_FRAGMENT_SIZE:
|
|
|
|
|
fprintf(fp, "fragment_size: %ld\n", (long)params->fragment_size);
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_HW_PARAM_FRAGMENTS:
|
|
|
|
|
fprintf(fp, "fragments: %d\n", params->fragments);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_dump_sw_params_fail(snd_pcm_sw_params_t *params, FILE *fp)
|
|
|
|
|
{
|
|
|
|
|
int k;
|
2000-11-29 08:32:36 +00:00
|
|
|
if (params->fail_mask == 0) {
|
|
|
|
|
fprintf(fp, "unknown sw_params failure reason\n");
|
2000-11-20 20:10:46 +00:00
|
|
|
return 0;
|
2000-11-29 08:32:36 +00:00
|
|
|
}
|
2000-11-20 20:10:46 +00:00
|
|
|
fprintf(fp, "sw_params failed on the following field value(s):\n");
|
|
|
|
|
for (k = 0; k <= SND_PCM_SW_PARAM_LAST; ++k) {
|
|
|
|
|
if (!(params->fail_mask & (1U << k)))
|
|
|
|
|
continue;
|
|
|
|
|
switch (k) {
|
|
|
|
|
case SND_PCM_SW_PARAM_START_MODE:
|
2000-11-29 08:32:36 +00:00
|
|
|
fprintf(fp, "start_mode: %s\n", assoc(params->start_mode, snd_pcm_start_mode_names));
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
|
|
|
|
case SND_PCM_SW_PARAM_READY_MODE:
|
2000-11-29 08:32:36 +00:00
|
|
|
fprintf(fp, "ready_mode: %s\n", assoc(params->ready_mode, snd_pcm_ready_mode_names));
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
|
|
|
|
case SND_PCM_SW_PARAM_XRUN_MODE:
|
2000-11-29 08:32:36 +00:00
|
|
|
fprintf(fp, "xrun_mode: %s\n", assoc(params->xrun_mode, snd_pcm_xrun_mode_names));
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
|
|
|
|
case SND_PCM_SW_PARAM_AVAIL_MIN:
|
|
|
|
|
fprintf(fp, "avail_min: %ld\n", (long)params->avail_min);
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_SW_PARAM_XFER_MIN:
|
|
|
|
|
fprintf(fp, "xfer_min: %ld\n", (long)params->xfer_min);
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_SW_PARAM_XFER_ALIGN:
|
|
|
|
|
fprintf(fp, "xfer_align: %ld\n", (long)params->xfer_align);
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_SW_PARAM_TIME:
|
|
|
|
|
fprintf(fp, "time: %d\n", params->time);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_dump_status(snd_pcm_status_t *status, FILE *fp)
|
|
|
|
|
{
|
|
|
|
|
assert(status);
|
2000-11-29 08:32:36 +00:00
|
|
|
fprintf(fp, "state : %s\n", assoc(status->state, snd_pcm_state_names));
|
2000-09-24 09:57:26 +00:00
|
|
|
fprintf(fp, "trigger_time: %ld.%06ld\n",
|
|
|
|
|
status->trigger_time.tv_sec, status->trigger_time.tv_usec);
|
|
|
|
|
fprintf(fp, "tstamp : %ld.%06ld\n",
|
|
|
|
|
status->tstamp.tv_sec, status->tstamp.tv_usec);
|
|
|
|
|
fprintf(fp, "delay : %ld\n", (long)status->delay);
|
|
|
|
|
fprintf(fp, "avail : %ld\n", (long)status->avail);
|
2000-10-05 10:26:07 +00:00
|
|
|
fprintf(fp, "avail_max : %ld\n", (long)status->avail_max);
|
2000-05-25 08:36:58 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_dump(snd_pcm_t *pcm, FILE *fp)
|
2000-07-17 15:33:29 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-07-17 15:33:29 +00:00
|
|
|
assert(fp);
|
2000-09-24 09:57:26 +00:00
|
|
|
pcm->ops->dump(pcm->op_arg, fp);
|
2000-07-17 15:33:29 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
const char *snd_pcm_format_name(unsigned int format)
|
2000-05-25 08:36:58 +00:00
|
|
|
{
|
2000-11-29 08:32:36 +00:00
|
|
|
assert(format <= SND_PCM_FORMAT_LAST);
|
|
|
|
|
return snd_pcm_format_names[format];
|
2000-05-25 08:36:58 +00:00
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
const char *snd_pcm_format_description(unsigned int format)
|
2000-05-25 08:36:58 +00:00
|
|
|
{
|
2000-11-29 08:32:36 +00:00
|
|
|
assert(format <= SND_PCM_FORMAT_LAST);
|
|
|
|
|
return snd_pcm_format_descriptions[format];
|
2000-05-25 08:36:58 +00:00
|
|
|
}
|
|
|
|
|
|
2000-07-17 15:33:29 +00:00
|
|
|
int snd_pcm_format_value(const char* name)
|
2000-05-25 08:36:58 +00:00
|
|
|
{
|
2000-11-29 08:32:36 +00:00
|
|
|
unsigned int format;
|
|
|
|
|
for (format = 0; format <= SND_PCM_FORMAT_LAST; format++)
|
|
|
|
|
if (snd_pcm_format_names[format] &&
|
|
|
|
|
strcasecmp(name, snd_pcm_format_names[format]) == 0)
|
|
|
|
|
return format;
|
2000-05-25 08:36:58 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
ssize_t snd_pcm_bytes_to_frames(snd_pcm_t *pcm, ssize_t bytes)
|
2000-06-10 12:39:51 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-09-24 09:57:26 +00:00
|
|
|
return bytes * 8 / pcm->bits_per_frame;
|
2000-06-10 12:39:51 +00:00
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
ssize_t snd_pcm_frames_to_bytes(snd_pcm_t *pcm, ssize_t frames)
|
2000-06-10 12:39:51 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-09-24 09:57:26 +00:00
|
|
|
return frames * pcm->bits_per_frame / 8;
|
2000-06-10 12:39:51 +00:00
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
ssize_t snd_pcm_bytes_to_samples(snd_pcm_t *pcm, ssize_t bytes)
|
2000-06-10 12:39:51 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-09-24 09:57:26 +00:00
|
|
|
return bytes * 8 / pcm->bits_per_sample;
|
2000-06-10 12:39:51 +00:00
|
|
|
}
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
ssize_t snd_pcm_samples_to_bytes(snd_pcm_t *pcm, ssize_t samples)
|
2000-06-10 12:39:51 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2000-09-24 09:57:26 +00:00
|
|
|
return samples * pcm->bits_per_sample / 8;
|
2000-06-10 12:39:51 +00:00
|
|
|
}
|
2000-06-21 14:59:20 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_open(snd_pcm_t **pcmp, char *name,
|
|
|
|
|
int stream, int mode)
|
2000-08-24 17:07:44 +00:00
|
|
|
{
|
|
|
|
|
char *str;
|
|
|
|
|
int err;
|
2000-09-24 09:57:26 +00:00
|
|
|
snd_config_t *pcm_conf, *conf, *type_conf;
|
|
|
|
|
snd_config_iterator_t i;
|
|
|
|
|
char *lib = NULL, *open = NULL;
|
|
|
|
|
int (*open_func)(snd_pcm_t **pcmp, char *name, snd_config_t *conf,
|
|
|
|
|
int stream, int mode);
|
|
|
|
|
void *h;
|
|
|
|
|
assert(pcmp && name);
|
|
|
|
|
err = snd_config_update();
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
err = snd_config_searchv(snd_config, &pcm_conf, "pcm", name, 0);
|
2000-10-14 10:31:34 +00:00
|
|
|
if (err < 0) {
|
2000-11-20 20:10:46 +00:00
|
|
|
int card, dev, subdev;
|
|
|
|
|
char socket[256], sname[256];
|
|
|
|
|
char format[16], file[256];
|
|
|
|
|
err = sscanf(name, "hw:%d,%d,%d", &card, &dev, &subdev);
|
|
|
|
|
if (err == 3)
|
|
|
|
|
return snd_pcm_hw_open(pcmp, name, card, dev, subdev, stream, mode);
|
|
|
|
|
err = sscanf(name, "hw:%d,%d", &card, &dev);
|
|
|
|
|
if (err == 2)
|
|
|
|
|
return snd_pcm_hw_open(pcmp, name, card, dev, -1, stream, mode);
|
|
|
|
|
err = sscanf(name, "plug:%d,%d,%d", &card, &dev, &subdev);
|
|
|
|
|
if (err == 3)
|
|
|
|
|
return snd_pcm_plug_open_hw(pcmp, name, card, dev, subdev, stream, mode);
|
|
|
|
|
err = sscanf(name, "plug:%d,%d", &card, &dev);
|
|
|
|
|
if (err == 2)
|
|
|
|
|
return snd_pcm_plug_open_hw(pcmp, name, card, dev, -1, stream, mode);
|
|
|
|
|
err = sscanf(name, "shm:%256s,%256s", socket, sname);
|
|
|
|
|
if (err == 2)
|
|
|
|
|
return snd_pcm_shm_open(pcmp, NULL, socket, sname, stream, mode);
|
|
|
|
|
err = sscanf(name, "file:%256s,%16s", file, format);
|
|
|
|
|
if (err == 2) {
|
|
|
|
|
snd_pcm_t *slave;
|
|
|
|
|
err = snd_pcm_null_open(&slave, NULL, stream, mode);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
return snd_pcm_file_open(pcmp, NULL, file, -1, format, slave, 1);
|
|
|
|
|
}
|
|
|
|
|
err = sscanf(name, "file:%256s", file);
|
|
|
|
|
if (err == 1) {
|
|
|
|
|
snd_pcm_t *slave;
|
|
|
|
|
err = snd_pcm_null_open(&slave, NULL, stream, mode);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
return snd_pcm_file_open(pcmp, NULL, file, -1, "raw", slave, 1);
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(name, "null") == 0)
|
|
|
|
|
return snd_pcm_null_open(pcmp, NULL, stream, mode);
|
2000-10-14 10:31:34 +00:00
|
|
|
ERR("Unknown PCM %s", name);
|
2000-11-20 20:10:46 +00:00
|
|
|
return -ENOENT;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
|
|
|
|
if (snd_config_type(pcm_conf) != SND_CONFIG_TYPE_COMPOUND) {
|
2000-11-30 09:40:50 +00:00
|
|
|
ERR("Invalid type for PCM %s definition", name);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
err = snd_config_search(pcm_conf, "stream", &conf);
|
|
|
|
|
if (err >= 0) {
|
|
|
|
|
err = snd_config_string_get(conf, &str);
|
2000-10-14 10:31:34 +00:00
|
|
|
if (err < 0) {
|
2000-11-30 09:40:50 +00:00
|
|
|
ERR("Invalid type for %s", conf->id);
|
2000-09-24 09:57:26 +00:00
|
|
|
return err;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
if (strcmp(str, "playback") == 0) {
|
|
|
|
|
if (stream != SND_PCM_STREAM_PLAYBACK)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
} else if (strcmp(str, "capture") == 0) {
|
|
|
|
|
if (stream != SND_PCM_STREAM_CAPTURE)
|
|
|
|
|
return -EINVAL;
|
2000-10-14 10:31:34 +00:00
|
|
|
} else {
|
2000-11-30 09:40:50 +00:00
|
|
|
ERR("Invalid value for %s", conf->id);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
err = snd_config_search(pcm_conf, "type", &conf);
|
2000-10-14 10:31:34 +00:00
|
|
|
if (err < 0) {
|
|
|
|
|
ERR("type is not defined");
|
2000-09-24 09:57:26 +00:00
|
|
|
return err;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
err = snd_config_string_get(conf, &str);
|
2000-10-14 10:31:34 +00:00
|
|
|
if (err < 0) {
|
2000-11-30 09:40:50 +00:00
|
|
|
ERR("Invalid type for %s", conf->id);
|
2000-09-24 09:57:26 +00:00
|
|
|
return err;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
err = snd_config_searchv(snd_config, &type_conf, "pcmtype", str, 0);
|
2000-10-14 10:31:34 +00:00
|
|
|
if (err < 0) {
|
|
|
|
|
ERR("Unknown PCM type %s", str);
|
2000-09-24 09:57:26 +00:00
|
|
|
return err;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
snd_config_foreach(i, type_conf) {
|
2000-08-24 17:07:44 +00:00
|
|
|
snd_config_t *n = snd_config_entry(i);
|
|
|
|
|
if (strcmp(n->id, "comment") == 0)
|
|
|
|
|
continue;
|
2000-09-24 09:57:26 +00:00
|
|
|
if (strcmp(n->id, "lib") == 0) {
|
|
|
|
|
err = snd_config_string_get(n, &lib);
|
2000-10-14 10:31:34 +00:00
|
|
|
if (err < 0) {
|
2000-11-30 09:40:50 +00:00
|
|
|
ERR("Invalid type for %s", n->id);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2000-08-24 17:07:44 +00:00
|
|
|
continue;
|
|
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
if (strcmp(n->id, "open") == 0) {
|
|
|
|
|
err = snd_config_string_get(n, &open);
|
2000-10-14 10:31:34 +00:00
|
|
|
if (err < 0) {
|
2000-11-30 09:40:50 +00:00
|
|
|
ERR("Invalid type for %s", n->id);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2000-08-24 17:07:44 +00:00
|
|
|
continue;
|
2000-11-30 09:40:50 +00:00
|
|
|
ERR("Unknown field %s", n->id);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -EINVAL;
|
2000-08-24 17:07:44 +00:00
|
|
|
}
|
|
|
|
|
}
|
2000-10-14 10:31:34 +00:00
|
|
|
if (!open) {
|
|
|
|
|
ERR("open is not defined");
|
2000-08-24 17:07:44 +00:00
|
|
|
return -EINVAL;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
if (!lib)
|
|
|
|
|
lib = "libasound.so";
|
|
|
|
|
h = dlopen(lib, RTLD_NOW);
|
2000-10-14 10:31:34 +00:00
|
|
|
if (!h) {
|
|
|
|
|
ERR("Cannot open shared library %s", lib);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -ENOENT;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
open_func = dlsym(h, open);
|
|
|
|
|
dlclose(h);
|
2000-10-14 10:31:34 +00:00
|
|
|
if (!open_func) {
|
|
|
|
|
ERR("symbol %s is not defined inside %s", open, lib);
|
2000-09-24 09:57:26 +00:00
|
|
|
return -ENXIO;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
return open_func(pcmp, name, pcm_conf, stream, mode);
|
2000-08-24 17:07:44 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
|
|
|
|
|
void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
|
|
|
|
|
void *buf)
|
2000-08-24 17:07:44 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
unsigned int channel;
|
2000-11-20 20:10:46 +00:00
|
|
|
unsigned int channels = pcm->channels;
|
2000-09-24 09:57:26 +00:00
|
|
|
for (channel = 0; channel < channels; ++channel, ++areas) {
|
|
|
|
|
areas->addr = buf;
|
|
|
|
|
areas->first = channel * pcm->bits_per_sample;
|
|
|
|
|
areas->step = pcm->bits_per_frame;
|
2000-08-24 17:07:44 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
|
|
|
|
|
void **bufs)
|
|
|
|
|
{
|
|
|
|
|
unsigned int channel;
|
2000-11-20 20:10:46 +00:00
|
|
|
unsigned int channels = pcm->channels;
|
2000-09-24 09:57:26 +00:00
|
|
|
for (channel = 0; channel < channels; ++channel, ++areas, ++bufs) {
|
|
|
|
|
areas->addr = *bufs;
|
|
|
|
|
areas->first = 0;
|
|
|
|
|
areas->step = pcm->bits_per_sample;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_wait(snd_pcm_t *pcm, int timeout)
|
|
|
|
|
{
|
|
|
|
|
struct pollfd pfd;
|
|
|
|
|
int err;
|
|
|
|
|
pfd.fd = snd_pcm_poll_descriptor(pcm);
|
|
|
|
|
pfd.events = pcm->stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
|
|
|
|
|
err = poll(&pfd, 1, timeout);
|
2000-08-26 08:10:53 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2000-09-24 09:57:26 +00:00
|
|
|
return 0;
|
2000-08-24 17:07:44 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
|
|
|
|
|
ssize_t snd_pcm_avail_update(snd_pcm_t *pcm)
|
2000-08-24 17:07:44 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
return pcm->fast_ops->avail_update(pcm->fast_op_arg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ssize_t snd_pcm_mmap_forward(snd_pcm_t *pcm, size_t size)
|
|
|
|
|
{
|
|
|
|
|
assert(size > 0);
|
2000-10-07 16:59:48 +00:00
|
|
|
assert(size <= snd_pcm_mmap_avail(pcm));
|
2000-09-24 09:57:26 +00:00
|
|
|
return pcm->fast_ops->mmap_forward(pcm->fast_op_arg, size);
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-30 14:15:52 +00:00
|
|
|
int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
|
2000-09-24 09:57:26 +00:00
|
|
|
size_t samples, int format)
|
|
|
|
|
{
|
|
|
|
|
/* FIXME: sub byte resolution and odd dst_offset */
|
|
|
|
|
char *dst;
|
|
|
|
|
unsigned int dst_step;
|
|
|
|
|
int width;
|
|
|
|
|
u_int64_t silence;
|
|
|
|
|
if (!dst_area->addr)
|
|
|
|
|
return 0;
|
|
|
|
|
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
|
|
|
|
width = snd_pcm_format_physical_width(format);
|
|
|
|
|
silence = snd_pcm_format_silence_64(format);
|
|
|
|
|
if (dst_area->step == (unsigned int) width) {
|
|
|
|
|
size_t dwords = samples * width / 64;
|
|
|
|
|
samples -= dwords * 64 / width;
|
|
|
|
|
while (dwords-- > 0)
|
|
|
|
|
*((u_int64_t*)dst)++ = silence;
|
|
|
|
|
if (samples == 0)
|
|
|
|
|
return 0;
|
2000-08-24 17:07:44 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
dst_step = dst_area->step / 8;
|
|
|
|
|
switch (width) {
|
|
|
|
|
case 4: {
|
|
|
|
|
u_int8_t s0 = silence & 0xf0;
|
|
|
|
|
u_int8_t s1 = silence & 0x0f;
|
|
|
|
|
int dstbit = dst_area->first % 8;
|
|
|
|
|
int dstbit_step = dst_area->step % 8;
|
|
|
|
|
while (samples-- > 0) {
|
|
|
|
|
if (dstbit) {
|
|
|
|
|
*dst &= 0xf0;
|
|
|
|
|
*dst |= s1;
|
|
|
|
|
} else {
|
|
|
|
|
*dst &= 0x0f;
|
|
|
|
|
*dst |= s0;
|
2000-08-26 08:10:53 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
dst += dst_step;
|
|
|
|
|
dstbit += dstbit_step;
|
|
|
|
|
if (dstbit == 8) {
|
|
|
|
|
dst++;
|
|
|
|
|
dstbit = 0;
|
2000-08-26 08:10:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 8: {
|
|
|
|
|
u_int8_t sil = silence;
|
|
|
|
|
while (samples-- > 0) {
|
|
|
|
|
*dst = sil;
|
|
|
|
|
dst += dst_step;
|
2000-08-26 08:10:53 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 16: {
|
|
|
|
|
u_int16_t sil = silence;
|
|
|
|
|
while (samples-- > 0) {
|
|
|
|
|
*(u_int16_t*)dst = sil;
|
|
|
|
|
dst += dst_step;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 32: {
|
|
|
|
|
u_int32_t sil = silence;
|
|
|
|
|
while (samples-- > 0) {
|
|
|
|
|
*(u_int32_t*)dst = sil;
|
|
|
|
|
dst += dst_step;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 64: {
|
|
|
|
|
while (samples-- > 0) {
|
|
|
|
|
*(u_int64_t*)dst = silence;
|
|
|
|
|
dst += dst_step;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-30 14:15:52 +00:00
|
|
|
int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_areas, size_t dst_offset,
|
2000-09-24 09:57:26 +00:00
|
|
|
size_t channels, size_t frames, int format)
|
|
|
|
|
{
|
|
|
|
|
int width = snd_pcm_format_physical_width(format);
|
|
|
|
|
while (channels > 0) {
|
|
|
|
|
void *addr = dst_areas->addr;
|
|
|
|
|
unsigned int step = dst_areas->step;
|
2000-11-30 14:15:52 +00:00
|
|
|
const snd_pcm_channel_area_t *begin = dst_areas;
|
2000-09-24 09:57:26 +00:00
|
|
|
int channels1 = channels;
|
|
|
|
|
unsigned int chns = 0;
|
|
|
|
|
int err;
|
|
|
|
|
while (1) {
|
|
|
|
|
channels1--;
|
|
|
|
|
chns++;
|
|
|
|
|
dst_areas++;
|
|
|
|
|
if (channels1 == 0 ||
|
|
|
|
|
dst_areas->addr != addr ||
|
|
|
|
|
dst_areas->step != step ||
|
|
|
|
|
dst_areas->first != dst_areas[-1].first + width)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (chns > 1 && chns * width == step) {
|
|
|
|
|
/* Collapse the areas */
|
|
|
|
|
snd_pcm_channel_area_t d;
|
|
|
|
|
d.addr = begin->addr;
|
|
|
|
|
d.first = begin->first;
|
|
|
|
|
d.step = width;
|
|
|
|
|
err = snd_pcm_area_silence(&d, dst_offset * chns, frames * chns, format);
|
|
|
|
|
channels -= chns;
|
|
|
|
|
} else {
|
|
|
|
|
err = snd_pcm_area_silence(begin, dst_offset, frames, format);
|
|
|
|
|
dst_areas = begin + 1;
|
|
|
|
|
channels--;
|
|
|
|
|
}
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2000-11-30 14:15:52 +00:00
|
|
|
int snd_pcm_area_copy(const snd_pcm_channel_area_t *src_area, size_t src_offset,
|
|
|
|
|
const snd_pcm_channel_area_t *dst_area, size_t dst_offset,
|
2000-09-24 09:57:26 +00:00
|
|
|
size_t samples, int format)
|
|
|
|
|
{
|
|
|
|
|
/* FIXME: sub byte resolution and odd dst_offset */
|
|
|
|
|
char *src, *dst;
|
|
|
|
|
int width;
|
|
|
|
|
int src_step, dst_step;
|
|
|
|
|
if (!src_area->addr)
|
|
|
|
|
return snd_pcm_area_silence(dst_area, dst_offset, samples, format);
|
|
|
|
|
src = snd_pcm_channel_area_addr(src_area, src_offset);
|
|
|
|
|
if (!dst_area->addr)
|
|
|
|
|
return 0;
|
|
|
|
|
dst = snd_pcm_channel_area_addr(dst_area, dst_offset);
|
|
|
|
|
width = snd_pcm_format_physical_width(format);
|
|
|
|
|
if (src_area->step == (unsigned int) width &&
|
|
|
|
|
dst_area->step == (unsigned int) width) {
|
|
|
|
|
size_t bytes = samples * width / 8;
|
|
|
|
|
samples -= bytes * 8 / width;
|
|
|
|
|
memcpy(dst, src, bytes);
|
|
|
|
|
if (samples == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
src_step = src_area->step / 8;
|
|
|
|
|
dst_step = dst_area->step / 8;
|
|
|
|
|
switch (width) {
|
|
|
|
|
case 4: {
|
|
|
|
|
int srcbit = src_area->first % 8;
|
|
|
|
|
int srcbit_step = src_area->step % 8;
|
|
|
|
|
int dstbit = dst_area->first % 8;
|
|
|
|
|
int dstbit_step = dst_area->step % 8;
|
|
|
|
|
while (samples-- > 0) {
|
|
|
|
|
unsigned char srcval;
|
|
|
|
|
if (srcbit)
|
|
|
|
|
srcval = *src & 0x0f;
|
|
|
|
|
else
|
|
|
|
|
srcval = *src & 0xf0;
|
|
|
|
|
if (dstbit)
|
|
|
|
|
*dst &= 0xf0;
|
|
|
|
|
else
|
|
|
|
|
*dst &= 0x0f;
|
|
|
|
|
*dst |= srcval;
|
|
|
|
|
src += src_step;
|
|
|
|
|
srcbit += srcbit_step;
|
|
|
|
|
if (srcbit == 8) {
|
|
|
|
|
src++;
|
|
|
|
|
srcbit = 0;
|
2000-08-26 08:10:53 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
dst += dst_step;
|
|
|
|
|
dstbit += dstbit_step;
|
|
|
|
|
if (dstbit == 8) {
|
|
|
|
|
dst++;
|
|
|
|
|
dstbit = 0;
|
2000-08-26 08:10:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 8: {
|
|
|
|
|
while (samples-- > 0) {
|
|
|
|
|
*dst = *src;
|
|
|
|
|
src += src_step;
|
|
|
|
|
dst += dst_step;
|
2000-08-26 08:10:53 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 16: {
|
|
|
|
|
while (samples-- > 0) {
|
|
|
|
|
*(u_int16_t*)dst = *(u_int16_t*)src;
|
|
|
|
|
src += src_step;
|
|
|
|
|
dst += dst_step;
|
2000-08-26 08:10:53 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 32: {
|
|
|
|
|
while (samples-- > 0) {
|
|
|
|
|
*(u_int32_t*)dst = *(u_int32_t*)src;
|
|
|
|
|
src += src_step;
|
|
|
|
|
dst += dst_step;
|
2000-08-26 08:10:53 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 64: {
|
|
|
|
|
while (samples-- > 0) {
|
|
|
|
|
*(u_int64_t*)dst = *(u_int64_t*)src;
|
|
|
|
|
src += src_step;
|
|
|
|
|
dst += dst_step;
|
2000-08-26 08:10:53 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
break;
|
2000-08-26 08:10:53 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
2000-08-24 17:07:44 +00:00
|
|
|
}
|
|
|
|
|
|
2000-11-30 14:15:52 +00:00
|
|
|
int snd_pcm_areas_copy(const snd_pcm_channel_area_t *src_areas, size_t src_offset,
|
|
|
|
|
const snd_pcm_channel_area_t *dst_areas, size_t dst_offset,
|
2000-09-24 09:57:26 +00:00
|
|
|
size_t channels, size_t frames, int format)
|
2000-08-31 11:21:05 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
int width = snd_pcm_format_physical_width(format);
|
|
|
|
|
while (channels > 0) {
|
|
|
|
|
unsigned int step = src_areas->step;
|
|
|
|
|
void *src_addr = src_areas->addr;
|
2000-11-30 14:15:52 +00:00
|
|
|
const snd_pcm_channel_area_t *src_start = src_areas;
|
2000-09-24 09:57:26 +00:00
|
|
|
void *dst_addr = dst_areas->addr;
|
2000-11-30 14:15:52 +00:00
|
|
|
const snd_pcm_channel_area_t *dst_start = dst_areas;
|
2000-09-24 09:57:26 +00:00
|
|
|
int channels1 = channels;
|
|
|
|
|
unsigned int chns = 0;
|
|
|
|
|
while (dst_areas->step == step) {
|
|
|
|
|
channels1--;
|
|
|
|
|
chns++;
|
|
|
|
|
src_areas++;
|
|
|
|
|
dst_areas++;
|
|
|
|
|
if (channels1 == 0 ||
|
|
|
|
|
src_areas->step != step ||
|
|
|
|
|
src_areas->addr != src_addr ||
|
|
|
|
|
dst_areas->addr != dst_addr ||
|
|
|
|
|
src_areas->first != src_areas[-1].first + width ||
|
|
|
|
|
dst_areas->first != dst_areas[-1].first + width)
|
|
|
|
|
break;
|
2000-08-31 11:21:05 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
if (chns > 1 && chns * width == step) {
|
|
|
|
|
/* Collapse the areas */
|
|
|
|
|
snd_pcm_channel_area_t s, d;
|
|
|
|
|
s.addr = src_start->addr;
|
|
|
|
|
s.first = src_start->first;
|
|
|
|
|
s.step = width;
|
|
|
|
|
d.addr = dst_start->addr;
|
|
|
|
|
d.first = dst_start->first;
|
|
|
|
|
d.step = width;
|
|
|
|
|
snd_pcm_area_copy(&s, src_offset * chns, &d, dst_offset * chns, frames * chns, format);
|
|
|
|
|
channels -= chns;
|
|
|
|
|
} else {
|
|
|
|
|
snd_pcm_area_copy(src_start, src_offset, dst_start, dst_offset, frames, format);
|
|
|
|
|
src_areas = src_start + 1;
|
|
|
|
|
dst_areas = dst_start + 1;
|
|
|
|
|
channels--;
|
2000-08-31 11:21:05 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-30 14:15:52 +00:00
|
|
|
ssize_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
|
2000-09-24 09:57:26 +00:00
|
|
|
size_t offset, size_t size,
|
|
|
|
|
snd_pcm_xfer_areas_func_t func)
|
|
|
|
|
{
|
|
|
|
|
size_t xfer = 0;
|
|
|
|
|
ssize_t err = 0;
|
|
|
|
|
int state = snd_pcm_state(pcm);
|
|
|
|
|
assert(size > 0);
|
|
|
|
|
assert(state >= SND_PCM_STATE_PREPARED);
|
|
|
|
|
if (state == SND_PCM_STATE_PREPARED &&
|
2000-11-20 20:10:46 +00:00
|
|
|
pcm->start_mode != SND_PCM_START_EXPLICIT) {
|
2000-09-24 09:57:26 +00:00
|
|
|
err = snd_pcm_start(pcm);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
state = SND_PCM_STATE_RUNNING;
|
|
|
|
|
}
|
|
|
|
|
while (xfer < size) {
|
|
|
|
|
ssize_t avail;
|
|
|
|
|
size_t frames;
|
|
|
|
|
again:
|
|
|
|
|
avail = snd_pcm_avail_update(pcm);
|
|
|
|
|
if (avail < 0) {
|
|
|
|
|
err = avail;
|
|
|
|
|
break;
|
2000-08-31 11:21:05 +00:00
|
|
|
}
|
2000-11-20 20:10:46 +00:00
|
|
|
if ((size_t)avail < pcm->avail_min) {
|
2000-09-24 09:57:26 +00:00
|
|
|
if (state != SND_PCM_STATE_RUNNING) {
|
|
|
|
|
err = -EPIPE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (pcm->mode & SND_PCM_NONBLOCK) {
|
|
|
|
|
err = -EAGAIN;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
err = snd_pcm_wait(pcm, -1);
|
2000-08-31 11:21:05 +00:00
|
|
|
if (err < 0)
|
2000-09-24 09:57:26 +00:00
|
|
|
break;
|
|
|
|
|
state = snd_pcm_state(pcm);
|
|
|
|
|
goto again;
|
2000-08-31 11:21:05 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
frames = size - xfer;
|
|
|
|
|
if (frames > (size_t)avail)
|
|
|
|
|
frames = avail;
|
|
|
|
|
err = func(pcm, areas, offset, frames, 0);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
break;
|
|
|
|
|
assert((size_t)err == frames);
|
|
|
|
|
xfer += err;
|
|
|
|
|
offset += err;
|
2000-08-31 11:21:05 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
if (xfer > 0)
|
|
|
|
|
return xfer;
|
|
|
|
|
return err;
|
2000-08-31 11:21:05 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2000-11-30 14:15:52 +00:00
|
|
|
ssize_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
|
2000-09-24 09:57:26 +00:00
|
|
|
size_t offset, size_t size,
|
|
|
|
|
snd_pcm_xfer_areas_func_t func)
|
2000-08-24 17:07:44 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
size_t xfer = 0;
|
|
|
|
|
ssize_t err = 0;
|
|
|
|
|
int state = snd_pcm_state(pcm);
|
|
|
|
|
assert(size > 0);
|
|
|
|
|
assert(state >= SND_PCM_STATE_PREPARED);
|
|
|
|
|
while (xfer < size) {
|
|
|
|
|
ssize_t avail;
|
|
|
|
|
size_t frames;
|
|
|
|
|
again:
|
|
|
|
|
if (state == SND_PCM_STATE_XRUN) {
|
|
|
|
|
err = -EPIPE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
avail = snd_pcm_avail_update(pcm);
|
|
|
|
|
if (avail < 0) {
|
|
|
|
|
err = avail;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2000-11-20 20:10:46 +00:00
|
|
|
if ((size_t)avail < pcm->avail_min) {
|
2000-09-24 09:57:26 +00:00
|
|
|
if (state != SND_PCM_STATE_RUNNING) {
|
|
|
|
|
err = -EPIPE;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (pcm->mode & SND_PCM_NONBLOCK) {
|
|
|
|
|
err = -EAGAIN;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
err = snd_pcm_wait(pcm, -1);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
break;
|
|
|
|
|
state = snd_pcm_state(pcm);
|
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
frames = size - xfer;
|
|
|
|
|
if (frames > (size_t)avail)
|
|
|
|
|
frames = avail;
|
|
|
|
|
err = func(pcm, areas, offset, frames, 0);
|
2000-08-24 17:07:44 +00:00
|
|
|
if (err < 0)
|
2000-09-24 09:57:26 +00:00
|
|
|
break;
|
|
|
|
|
assert((size_t)err == frames);
|
|
|
|
|
xfer += err;
|
|
|
|
|
offset += err;
|
|
|
|
|
if (state == SND_PCM_STATE_PREPARED &&
|
2000-11-20 20:10:46 +00:00
|
|
|
pcm->start_mode != SND_PCM_START_EXPLICIT) {
|
2000-09-24 09:57:26 +00:00
|
|
|
err = snd_pcm_start(pcm);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
break;
|
2000-10-14 18:34:51 +00:00
|
|
|
state = SND_PCM_STATE_RUNNING;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2000-08-24 17:07:44 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
if (xfer > 0)
|
|
|
|
|
return xfer;
|
|
|
|
|
return err;
|
2000-08-24 17:07:44 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2000-11-20 20:10:46 +00:00
|
|
|
#if 0
|
2000-10-20 09:18:13 +00:00
|
|
|
int snd_pcm_alloc_user_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i)
|
|
|
|
|
{
|
|
|
|
|
i->type = SND_PCM_MMAP_USER;
|
2000-11-20 20:10:46 +00:00
|
|
|
i->size = snd_pcm_frames_to_bytes(pcm, pcm->buffer_size);
|
2000-10-20 09:18:13 +00:00
|
|
|
i->u.user.shmid = shmget(IPC_PRIVATE, i->size, 0666);
|
|
|
|
|
if (i->u.user.shmid < 0) {
|
|
|
|
|
SYSERR("shmget failed");
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
i->addr = shmat(i->u.user.shmid, 0, 0);
|
|
|
|
|
if (i->addr == (void*) -1) {
|
|
|
|
|
SYSERR("shmat failed");
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_alloc_kernel_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i, int fd)
|
|
|
|
|
{
|
|
|
|
|
i->type = SND_PCM_MMAP_KERNEL;
|
2000-11-20 20:10:46 +00:00
|
|
|
/* FIXME */
|
|
|
|
|
i->size = PAGE_ALIGN(snd_pcm_frames_to_bytes(pcm, pcm->buffer_size));
|
|
|
|
|
i->addr = mmap(NULL, i->size,
|
2000-10-20 09:18:13 +00:00
|
|
|
PROT_WRITE | PROT_READ,
|
|
|
|
|
MAP_FILE|MAP_SHARED,
|
|
|
|
|
fd, SND_PCM_MMAP_OFFSET_DATA);
|
|
|
|
|
if (i->addr == MAP_FAILED ||
|
|
|
|
|
i->addr == NULL) {
|
|
|
|
|
SYSERR("data mmap failed");
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
i->u.kernel.fd = fd;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_free_mmap(snd_pcm_t *pcm, snd_pcm_mmap_info_t *i)
|
|
|
|
|
{
|
|
|
|
|
if (i->type == SND_PCM_MMAP_USER) {
|
|
|
|
|
if (shmdt(i->addr) < 0) {
|
|
|
|
|
SYSERR("shmdt failed");
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
if (shmctl(i->u.user.shmid, IPC_RMID, 0) < 0) {
|
|
|
|
|
SYSERR("shmctl IPC_RMID failed");
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (munmap(pcm->mmap_info->addr, pcm->mmap_info->size) < 0) {
|
|
|
|
|
SYSERR("data munmap failed");
|
|
|
|
|
return -errno;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2000-11-20 20:10:46 +00:00
|
|
|
#endif
|
2000-10-20 09:18:13 +00:00
|
|
|
|
2000-11-20 20:10:46 +00:00
|
|
|
int snd_pcm_hw_info_bits_per_sample(snd_pcm_hw_info_t *info,
|
|
|
|
|
unsigned int *min, unsigned int *max)
|
|
|
|
|
{
|
|
|
|
|
int k;
|
|
|
|
|
unsigned int bits_min = UINT_MAX, bits_max = 0;
|
|
|
|
|
int changed = 0;
|
|
|
|
|
for (k = 0; k <= SND_PCM_FORMAT_LAST; ++k) {
|
|
|
|
|
int bits;
|
|
|
|
|
if (!(info->format_mask & (1U << k)))
|
|
|
|
|
continue;
|
|
|
|
|
bits = snd_pcm_format_physical_width(k);
|
|
|
|
|
assert(bits > 0);
|
|
|
|
|
if ((unsigned) bits < *min || (unsigned)bits > *max) {
|
|
|
|
|
info->format_mask &= ~(1U << k);
|
|
|
|
|
changed++;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if ((unsigned)bits < bits_min)
|
|
|
|
|
bits_min = bits;
|
|
|
|
|
if ((unsigned)bits > bits_max)
|
|
|
|
|
bits_max = bits;
|
|
|
|
|
}
|
|
|
|
|
*min = bits_min;
|
|
|
|
|
*max = bits_max;
|
|
|
|
|
if (info->format_mask == 0)
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int snd_pcm_hw_info_complete(snd_pcm_hw_info_t *info)
|
|
|
|
|
{
|
|
|
|
|
if (info->msbits == 0) {
|
|
|
|
|
unsigned int bits_min = 0, bits_max = UINT_MAX;
|
|
|
|
|
snd_pcm_hw_info_bits_per_sample(info, &bits_min, &bits_max);
|
|
|
|
|
if (bits_min == bits_max)
|
|
|
|
|
info->msbits = bits_min;
|
|
|
|
|
}
|
2000-12-04 10:07:51 +00:00
|
|
|
if (info->rate_den == 0 &&
|
2000-11-20 20:10:46 +00:00
|
|
|
info->rate_min == info->rate_max) {
|
2000-12-04 10:07:51 +00:00
|
|
|
info->rate_num = info->rate_min;
|
|
|
|
|
info->rate_den = 1;
|
2000-11-20 20:10:46 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
int snd_pcm_hw_params_info(snd_pcm_t *pcm, snd_pcm_hw_params_t *params,
|
|
|
|
|
snd_pcm_hw_info_t *info)
|
2000-11-20 20:10:46 +00:00
|
|
|
{
|
2000-11-29 08:32:36 +00:00
|
|
|
int err = snd_pcm_hw_info_to_params(pcm, info, params);
|
|
|
|
|
assert(err >= 0);
|
|
|
|
|
return snd_pcm_hw_params(pcm, params);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct _snd_pcm_strategy {
|
|
|
|
|
unsigned int badness_min, badness_max;
|
|
|
|
|
int (*choose_param)(const snd_pcm_hw_info_t *info,
|
|
|
|
|
snd_pcm_t *pcm,
|
|
|
|
|
const snd_pcm_strategy_t *strategy);
|
2000-12-07 15:58:03 +00:00
|
|
|
int (*next_value)(const snd_pcm_hw_info_t *info,
|
2000-11-29 08:32:36 +00:00
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
int value,
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_t *pcm,
|
|
|
|
|
const snd_pcm_strategy_t *strategy);
|
|
|
|
|
int (*min_badness)(const snd_pcm_hw_info_t *info,
|
|
|
|
|
unsigned int max_badness,
|
|
|
|
|
snd_pcm_t *pcm,
|
|
|
|
|
const snd_pcm_strategy_t *strategy);
|
|
|
|
|
void *private;
|
|
|
|
|
void (*free)(snd_pcm_strategy_t *strategy);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Independent badness */
|
|
|
|
|
typedef struct _snd_pcm_strategy_simple snd_pcm_strategy_simple_t;
|
|
|
|
|
|
|
|
|
|
struct _snd_pcm_strategy_simple {
|
|
|
|
|
int valid;
|
|
|
|
|
unsigned int order;
|
2000-12-07 15:58:03 +00:00
|
|
|
int (*next_value)(const snd_pcm_hw_info_t *info,
|
2000-11-29 08:32:36 +00:00
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
int value,
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_t *pcm,
|
|
|
|
|
const snd_pcm_strategy_simple_t *par);
|
|
|
|
|
unsigned int (*min_badness)(const snd_pcm_hw_info_t *info,
|
|
|
|
|
unsigned int param,
|
|
|
|
|
snd_pcm_t *pcm,
|
|
|
|
|
const snd_pcm_strategy_simple_t *par);
|
|
|
|
|
void *private;
|
|
|
|
|
void (*free)(snd_pcm_strategy_simple_t *strategy);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct _snd_pcm_strategy_simple_near {
|
2000-12-07 15:58:03 +00:00
|
|
|
int best;
|
2000-11-29 08:32:36 +00:00
|
|
|
unsigned int mul;
|
|
|
|
|
} snd_pcm_strategy_simple_near_t;
|
|
|
|
|
|
|
|
|
|
typedef struct _snd_pcm_strategy_simple_choices {
|
|
|
|
|
unsigned int count;
|
|
|
|
|
/* choices need to be sorted on ascending badness */
|
|
|
|
|
snd_pcm_strategy_simple_choices_list_t *choices;
|
|
|
|
|
} snd_pcm_strategy_simple_choices_t;
|
|
|
|
|
|
|
|
|
|
static inline unsigned int hweight32(u_int32_t v)
|
|
|
|
|
{
|
|
|
|
|
v = (v & 0x55555555) + ((v >> 1) & 0x55555555);
|
|
|
|
|
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
|
|
|
|
|
v = (v & 0x0F0F0F0F) + ((v >> 4) & 0x0F0F0F0F);
|
|
|
|
|
v = (v & 0x00FF00FF) + ((v >> 8) & 0x00FF00FF);
|
|
|
|
|
return (v & 0x0000FFFF) + ((v >> 16) & 0x0000FFFF);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline unsigned int ld2(u_int32_t v)
|
|
|
|
|
{
|
|
|
|
|
unsigned r = 0;
|
|
|
|
|
|
|
|
|
|
if (v >= 0x10000) {
|
|
|
|
|
v >>= 16;
|
|
|
|
|
r += 16;
|
|
|
|
|
}
|
|
|
|
|
if (v >= 0x100) {
|
|
|
|
|
v >>= 8;
|
|
|
|
|
r += 8;
|
|
|
|
|
}
|
|
|
|
|
if (v >= 0x10) {
|
|
|
|
|
v >>= 4;
|
|
|
|
|
r += 4;
|
|
|
|
|
}
|
|
|
|
|
if (v >= 4) {
|
|
|
|
|
v >>= 2;
|
|
|
|
|
r += 2;
|
|
|
|
|
}
|
|
|
|
|
if (v >= 2)
|
|
|
|
|
r++;
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
enum { MASK, MINMAX } type;
|
|
|
|
|
char **names;
|
|
|
|
|
unsigned int last;
|
|
|
|
|
} par_desc_t;
|
|
|
|
|
|
2000-12-07 15:58:03 +00:00
|
|
|
par_desc_t hw_infos[SND_PCM_HW_INFO_LAST + 1] = {
|
|
|
|
|
[SND_PCM_HW_INFO_ACCESS] = {
|
2000-11-29 08:32:36 +00:00
|
|
|
type: MASK,
|
|
|
|
|
names: snd_pcm_access_names,
|
|
|
|
|
last: SND_PCM_ACCESS_LAST,
|
|
|
|
|
},
|
2000-12-07 15:58:03 +00:00
|
|
|
[SND_PCM_HW_INFO_FORMAT] = {
|
2000-11-29 08:32:36 +00:00
|
|
|
type: MASK,
|
|
|
|
|
names: snd_pcm_format_names,
|
|
|
|
|
last: SND_PCM_FORMAT_LAST,
|
|
|
|
|
},
|
2000-12-07 15:58:03 +00:00
|
|
|
[SND_PCM_HW_INFO_SUBFORMAT] = {
|
2000-11-29 08:32:36 +00:00
|
|
|
type: MASK,
|
|
|
|
|
names: snd_pcm_subformat_names,
|
|
|
|
|
last: SND_PCM_SUBFORMAT_LAST,
|
|
|
|
|
},
|
2000-12-07 15:58:03 +00:00
|
|
|
[SND_PCM_HW_INFO_CHANNELS] = {
|
2000-11-29 08:32:36 +00:00
|
|
|
type: MINMAX,
|
|
|
|
|
names: 0,
|
|
|
|
|
last: 0,
|
|
|
|
|
},
|
2000-12-07 15:58:03 +00:00
|
|
|
[SND_PCM_HW_INFO_RATE] = {
|
2000-11-29 08:32:36 +00:00
|
|
|
type: MINMAX,
|
|
|
|
|
names: 0,
|
|
|
|
|
last: 0,
|
|
|
|
|
},
|
2000-12-07 15:58:03 +00:00
|
|
|
[SND_PCM_HW_INFO_FRAGMENT_LENGTH] = {
|
2000-11-29 08:32:36 +00:00
|
|
|
type: MINMAX,
|
|
|
|
|
names: 0,
|
|
|
|
|
last: 0,
|
|
|
|
|
},
|
2000-12-07 15:58:03 +00:00
|
|
|
[SND_PCM_HW_INFO_FRAGMENTS] = {
|
2000-11-29 08:32:36 +00:00
|
|
|
type: MINMAX,
|
|
|
|
|
names: 0,
|
|
|
|
|
last: 0,
|
|
|
|
|
},
|
2000-12-07 15:58:03 +00:00
|
|
|
[SND_PCM_HW_INFO_BUFFER_LENGTH] = {
|
2000-11-29 08:32:36 +00:00
|
|
|
type: MINMAX,
|
|
|
|
|
names: 0,
|
|
|
|
|
last: 0,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
unsigned int snd_pcm_hw_info_par_get_mask(const snd_pcm_hw_info_t *info,
|
|
|
|
|
unsigned int param)
|
|
|
|
|
{
|
|
|
|
|
switch (param) {
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_ACCESS:
|
2000-11-29 08:32:36 +00:00
|
|
|
return info->access_mask;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_FORMAT:
|
2000-11-29 08:32:36 +00:00
|
|
|
return info->format_mask;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_SUBFORMAT:
|
2000-11-29 08:32:36 +00:00
|
|
|
return info->subformat_mask;
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void snd_pcm_hw_info_par_get_minmax(const snd_pcm_hw_info_t *info,
|
|
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int *min, unsigned int *max)
|
2000-11-29 08:32:36 +00:00
|
|
|
{
|
|
|
|
|
switch (param) {
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_ACCESS:
|
|
|
|
|
case SND_PCM_HW_INFO_FORMAT:
|
|
|
|
|
case SND_PCM_HW_INFO_SUBFORMAT:
|
2000-11-29 08:32:36 +00:00
|
|
|
{
|
|
|
|
|
unsigned int mask = snd_pcm_hw_info_par_get_mask(info, param);
|
|
|
|
|
if (!mask) {
|
|
|
|
|
*min = 32;
|
|
|
|
|
*max = 0;
|
|
|
|
|
} else {
|
|
|
|
|
*min = ffs(mask) - 1;
|
|
|
|
|
*max = ld2(mask);
|
|
|
|
|
}
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
2000-11-29 08:32:36 +00:00
|
|
|
}
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_CHANNELS:
|
2000-11-29 08:32:36 +00:00
|
|
|
*min = info->channels_min;
|
|
|
|
|
*max = info->channels_max;
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_RATE:
|
2000-11-29 08:32:36 +00:00
|
|
|
*min = info->rate_min;
|
|
|
|
|
*max = info->rate_max;
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_FRAGMENT_LENGTH:
|
|
|
|
|
*min = info->fragment_length_min;
|
|
|
|
|
*max = info->fragment_length_max;
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_FRAGMENTS:
|
2000-11-29 08:32:36 +00:00
|
|
|
*min = info->fragments_min;
|
|
|
|
|
*max = info->fragments_max;
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_BUFFER_LENGTH:
|
|
|
|
|
*min = info->buffer_length_min;
|
|
|
|
|
*max = info->buffer_length_max;
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
void snd_pcm_hw_info_par_set_mask(snd_pcm_hw_info_t *info, unsigned int param,
|
|
|
|
|
unsigned int v)
|
2000-11-20 20:10:46 +00:00
|
|
|
{
|
2000-11-29 08:32:36 +00:00
|
|
|
switch (param) {
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_ACCESS:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->access_mask = v;
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_FORMAT:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->format_mask = v;
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_SUBFORMAT:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->subformat_mask = v;
|
2000-11-20 20:10:46 +00:00
|
|
|
break;
|
2000-11-26 12:16:18 +00:00
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
void snd_pcm_hw_info_par_set_minmax(snd_pcm_hw_info_t *info,
|
2000-11-26 12:16:18 +00:00
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int min, unsigned int max)
|
2000-11-26 12:16:18 +00:00
|
|
|
{
|
|
|
|
|
switch (param) {
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_ACCESS:
|
|
|
|
|
case SND_PCM_HW_INFO_FORMAT:
|
|
|
|
|
case SND_PCM_HW_INFO_SUBFORMAT:
|
2000-11-29 08:32:36 +00:00
|
|
|
{
|
|
|
|
|
unsigned int mask;
|
|
|
|
|
if (min >= 32 || max <= 0 || min > max) {
|
|
|
|
|
snd_pcm_hw_info_par_set_mask(info, param, 0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (max >= 31) {
|
|
|
|
|
max = 31;
|
|
|
|
|
if (min <= 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
mask = snd_pcm_hw_info_par_get_mask(info, param);
|
|
|
|
|
mask &= ((1U << (max - min + 1)) - 1) << min;
|
|
|
|
|
snd_pcm_hw_info_par_set_mask(info, param, mask);
|
|
|
|
|
break;
|
2000-11-26 12:16:18 +00:00
|
|
|
}
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_CHANNELS:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->channels_min = min;
|
|
|
|
|
info->channels_max = max;
|
|
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_RATE:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->rate_min = min;
|
|
|
|
|
info->rate_max = max;
|
|
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_FRAGMENT_LENGTH:
|
|
|
|
|
info->fragment_length_min = min;
|
|
|
|
|
info->fragment_length_max = max;
|
2000-11-29 08:32:36 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_FRAGMENTS:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->fragments_min = min;
|
|
|
|
|
info->fragments_max = max;
|
|
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_BUFFER_LENGTH:
|
|
|
|
|
info->buffer_length_min = min;
|
|
|
|
|
info->buffer_length_max = max;
|
2000-11-29 08:32:36 +00:00
|
|
|
break;
|
2000-11-26 12:16:18 +00:00
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
void snd_pcm_hw_info_par_copy(snd_pcm_hw_info_t *info, unsigned int param,
|
|
|
|
|
snd_pcm_hw_info_t *src)
|
2000-11-26 12:16:18 +00:00
|
|
|
{
|
|
|
|
|
switch (param) {
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_ACCESS:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->access_mask = src->access_mask;
|
2000-11-26 12:16:18 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_FORMAT:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->format_mask = src->format_mask;
|
2000-11-26 12:16:18 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_SUBFORMAT:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->subformat_mask = src->subformat_mask;
|
2000-11-26 12:16:18 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_CHANNELS:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->channels_min = src->channels_min;
|
|
|
|
|
info->channels_max = src->channels_max;
|
2000-11-26 12:16:18 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_RATE:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->rate_min = src->rate_min;
|
|
|
|
|
info->rate_max = src->rate_max;
|
2000-11-26 12:16:18 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_FRAGMENT_LENGTH:
|
|
|
|
|
info->fragment_length_min = src->fragment_length_min;
|
|
|
|
|
info->fragment_length_max = src->fragment_length_max;
|
2000-11-26 12:16:18 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_FRAGMENTS:
|
2000-11-29 08:32:36 +00:00
|
|
|
info->fragments_min = src->fragments_min;
|
|
|
|
|
info->fragments_max = src->fragments_max;
|
2000-11-26 12:16:18 +00:00
|
|
|
break;
|
2000-12-07 15:58:03 +00:00
|
|
|
case SND_PCM_HW_INFO_BUFFER_LENGTH:
|
|
|
|
|
info->buffer_length_min = src->buffer_length_min;
|
|
|
|
|
info->buffer_length_max = src->buffer_length_max;
|
2000-11-26 12:16:18 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int snd_pcm_hw_info_par_choices(const snd_pcm_hw_info_t *info,
|
2000-11-29 08:32:36 +00:00
|
|
|
unsigned int param)
|
2000-11-26 12:16:18 +00:00
|
|
|
{
|
2000-11-29 08:32:36 +00:00
|
|
|
par_desc_t *p;
|
2000-12-07 15:58:03 +00:00
|
|
|
assert(param <= SND_PCM_HW_INFO_LAST);
|
|
|
|
|
p = &hw_infos[param];
|
2000-11-29 08:32:36 +00:00
|
|
|
switch (p->type) {
|
|
|
|
|
case MASK:
|
|
|
|
|
return hweight32(snd_pcm_hw_info_par_get_mask(info, param));
|
|
|
|
|
case MINMAX:
|
|
|
|
|
{
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int min, max;
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
|
|
|
|
|
return max - min + 1;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int snd_pcm_hw_info_par_refine_min(snd_pcm_hw_info_t *info,
|
2000-11-29 08:32:36 +00:00
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int value)
|
2000-11-29 08:32:36 +00:00
|
|
|
{
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int min, max;
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
|
|
|
|
|
if (min < value) {
|
|
|
|
|
min = value;
|
|
|
|
|
snd_pcm_hw_info_par_set_minmax(info, param, min, max);
|
|
|
|
|
}
|
|
|
|
|
return min;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int snd_pcm_hw_info_par_refine_max(snd_pcm_hw_info_t *info,
|
2000-11-29 08:32:36 +00:00
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int value)
|
2000-11-29 08:32:36 +00:00
|
|
|
{
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int min, max;
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
|
|
|
|
|
if (max > value) {
|
|
|
|
|
max = value;
|
|
|
|
|
snd_pcm_hw_info_par_set_minmax(info, param, min, max);
|
|
|
|
|
}
|
|
|
|
|
return max;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_hw_info_par_check(const snd_pcm_hw_info_t *info,
|
|
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int value)
|
2000-11-29 08:32:36 +00:00
|
|
|
{
|
|
|
|
|
par_desc_t *p;
|
2000-12-07 15:58:03 +00:00
|
|
|
assert(param <= SND_PCM_HW_INFO_LAST);
|
|
|
|
|
p = &hw_infos[param];
|
2000-11-29 08:32:36 +00:00
|
|
|
switch (p->type) {
|
|
|
|
|
case MASK:
|
|
|
|
|
return snd_pcm_hw_info_par_get_mask(info, param) & (1 << value);
|
|
|
|
|
case MINMAX:
|
|
|
|
|
{
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int min, max;
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
|
|
|
|
|
return value >= min && value <= max;
|
|
|
|
|
}
|
2000-11-26 12:16:18 +00:00
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-07 15:58:03 +00:00
|
|
|
int snd_pcm_hw_info_par_nearest_next(const snd_pcm_hw_info_t *info,
|
2000-11-29 08:32:36 +00:00
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int best, int value,
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_t *pcm)
|
2000-11-26 12:16:18 +00:00
|
|
|
{
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int min, max;
|
|
|
|
|
unsigned int max1, min2;
|
2000-11-26 12:16:18 +00:00
|
|
|
snd_pcm_hw_info_t i1, i2;
|
|
|
|
|
int err1 = -EINVAL;
|
|
|
|
|
int err2 = -EINVAL;
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
|
2000-11-26 12:16:18 +00:00
|
|
|
i1 = *info;
|
|
|
|
|
i2 = *info;
|
|
|
|
|
if (value < 0) {
|
2000-12-04 10:07:51 +00:00
|
|
|
max1 = best;
|
|
|
|
|
min2 = best + 1;
|
2000-11-26 12:16:18 +00:00
|
|
|
} else {
|
2000-12-07 15:58:03 +00:00
|
|
|
int diff = value - best;
|
2000-11-26 12:16:18 +00:00
|
|
|
if (diff < 0) {
|
2000-12-04 10:07:51 +00:00
|
|
|
if (value > 1)
|
|
|
|
|
max1 = value - 1;
|
|
|
|
|
else
|
|
|
|
|
max1 = 0;
|
|
|
|
|
min2 = best - diff;
|
2000-11-26 12:16:18 +00:00
|
|
|
} else {
|
2000-12-07 15:58:03 +00:00
|
|
|
if (best > (unsigned int) diff)
|
2000-12-04 10:07:51 +00:00
|
|
|
max1 = best - diff - 1;
|
|
|
|
|
else
|
|
|
|
|
max1 = 0;
|
|
|
|
|
min2 = value + 1;
|
2000-11-26 12:16:18 +00:00
|
|
|
}
|
|
|
|
|
}
|
2000-11-29 08:32:36 +00:00
|
|
|
max1 = snd_pcm_hw_info_par_refine_max(&i1, param, max1);
|
|
|
|
|
min2 = snd_pcm_hw_info_par_refine_min(&i2, param, min2);
|
2000-11-26 12:16:18 +00:00
|
|
|
if (min <= max1) {
|
|
|
|
|
err1 = snd_pcm_hw_info(pcm, &i1);
|
|
|
|
|
if (err1 >= 0)
|
2000-11-29 08:32:36 +00:00
|
|
|
max1 = snd_pcm_hw_info_par_refine_max(&i1, param, max1);
|
2000-11-26 12:16:18 +00:00
|
|
|
}
|
|
|
|
|
if (min2 <= max && (err1 < 0 || best - max1 > min2 - best)) {
|
|
|
|
|
err2 = snd_pcm_hw_info(pcm, &i2);
|
|
|
|
|
if (err2 >= 0)
|
2000-11-29 08:32:36 +00:00
|
|
|
min2 = snd_pcm_hw_info_par_refine_min(&i2, param, min2);
|
2000-11-26 12:16:18 +00:00
|
|
|
}
|
|
|
|
|
if (err1 < 0) {
|
|
|
|
|
if (err2 < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
return min2;
|
|
|
|
|
} else if (err2 < 0)
|
|
|
|
|
return max1;
|
|
|
|
|
if (best - max1 <= min2 - best)
|
|
|
|
|
return max1;
|
|
|
|
|
return min2;
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
void snd_pcm_hw_info_par_dump(snd_pcm_hw_info_t *info, unsigned int param, FILE *fp)
|
|
|
|
|
{
|
|
|
|
|
par_desc_t *p;
|
2000-12-07 15:58:03 +00:00
|
|
|
assert(param <= SND_PCM_HW_INFO_LAST);
|
|
|
|
|
p = &hw_infos[param];
|
2000-11-29 08:32:36 +00:00
|
|
|
switch (p->type) {
|
|
|
|
|
case MASK:
|
|
|
|
|
{
|
|
|
|
|
unsigned int mask = snd_pcm_hw_info_par_get_mask(info, param);
|
|
|
|
|
if (mask == ~0U)
|
|
|
|
|
fputs(" ALL", fp);
|
|
|
|
|
else if (mask) {
|
|
|
|
|
unsigned int k;
|
|
|
|
|
for (k = 0; k <= p->last; ++k)
|
|
|
|
|
if (mask & (1U << k)) {
|
|
|
|
|
putc(' ', fp);
|
|
|
|
|
fputs(p->names[k], fp);
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
fputs(" NONE", fp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case MINMAX:
|
|
|
|
|
{
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int min, max;
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
|
2000-12-07 15:58:03 +00:00
|
|
|
printf("%d - %d", min, max);
|
2000-11-29 10:26:01 +00:00
|
|
|
break;
|
2000-11-29 08:32:36 +00:00
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_hw_info_par_empty(snd_pcm_hw_info_t *info, unsigned int param)
|
|
|
|
|
{
|
|
|
|
|
par_desc_t *p;
|
2000-12-07 15:58:03 +00:00
|
|
|
assert(param <= SND_PCM_HW_INFO_LAST);
|
|
|
|
|
p = &hw_infos[param];
|
2000-11-29 08:32:36 +00:00
|
|
|
switch (p->type) {
|
|
|
|
|
case MASK:
|
|
|
|
|
return !snd_pcm_hw_info_par_get_mask(info, param);
|
|
|
|
|
case MINMAX:
|
|
|
|
|
{
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int min, max;
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_par_get_minmax(info, param, &min, &max);
|
|
|
|
|
return min > max;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
assert(0);
|
|
|
|
|
return 0;;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int snd_pcm_hw_info_fail_mask(snd_pcm_hw_info_t *info)
|
|
|
|
|
{
|
|
|
|
|
unsigned int k, mask = 0;
|
2000-12-07 15:58:03 +00:00
|
|
|
for (k = 0; k <= SND_PCM_HW_INFO_LAST; ++k) {
|
2000-11-29 08:32:36 +00:00
|
|
|
if (snd_pcm_hw_info_par_empty(info, k))
|
|
|
|
|
mask |= 1 << k;
|
|
|
|
|
}
|
|
|
|
|
return mask;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int _snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
snd_pcm_hw_info_t info;
|
|
|
|
|
|
|
|
|
|
snd_pcm_hw_params_to_info(params, &info);
|
|
|
|
|
err = snd_pcm_hw_info(pcm, &info);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
params->fail_mask = snd_pcm_hw_info_fail_mask(&info);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2000-11-30 09:40:50 +00:00
|
|
|
params->fail_mask = 0;
|
2000-11-29 08:32:36 +00:00
|
|
|
|
|
|
|
|
if ((err = pcm->ops->hw_params(pcm->op_arg, params)) < 0)
|
|
|
|
|
return err;
|
|
|
|
|
pcm->setup = 1;
|
|
|
|
|
pcm->access = params->access;
|
|
|
|
|
pcm->format = params->format;
|
|
|
|
|
pcm->subformat = params->subformat;
|
|
|
|
|
pcm->rate = params->rate;
|
|
|
|
|
pcm->channels = params->channels;
|
|
|
|
|
pcm->fragment_size = params->fragment_size;
|
|
|
|
|
pcm->fragments = params->fragments;
|
|
|
|
|
pcm->bits_per_sample = snd_pcm_format_physical_width(params->format);
|
|
|
|
|
pcm->bits_per_frame = pcm->bits_per_sample * params->channels;
|
|
|
|
|
pcm->buffer_size = params->fragment_size * params->fragments;
|
|
|
|
|
|
|
|
|
|
pcm->info = info.info;
|
|
|
|
|
pcm->msbits = info.msbits;
|
2000-12-04 10:07:51 +00:00
|
|
|
pcm->rate_num = info.rate_num;
|
|
|
|
|
pcm->rate_den = info.rate_den;
|
2000-11-29 08:32:36 +00:00
|
|
|
pcm->fifo_size = info.fifo_size;
|
|
|
|
|
|
|
|
|
|
/* Default sw params */
|
|
|
|
|
pcm->start_mode = SND_PCM_START_DATA;
|
|
|
|
|
pcm->ready_mode = SND_PCM_READY_FRAGMENT;
|
|
|
|
|
pcm->xrun_mode = SND_PCM_XRUN_FRAGMENT;
|
|
|
|
|
pcm->avail_min = pcm->fragment_size;
|
|
|
|
|
pcm->xfer_min = pcm->fragment_size;
|
|
|
|
|
pcm->xfer_align = pcm->fragment_size;
|
|
|
|
|
pcm->time = 0;
|
|
|
|
|
pcm->boundary = LONG_MAX - pcm->buffer_size * 2 - LONG_MAX % pcm->buffer_size;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
assert(pcm && params);
|
|
|
|
|
if (pcm->setup && pcm->mmap_channels &&
|
|
|
|
|
(pcm->mmap_rw ||
|
|
|
|
|
(pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
|
|
|
|
|
pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED ||
|
|
|
|
|
pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX))) {
|
|
|
|
|
err = snd_pcm_munmap(pcm);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
err = _snd_pcm_hw_params(pcm, params);
|
|
|
|
|
if (pcm->setup &&
|
|
|
|
|
(pcm->mmap_rw ||
|
|
|
|
|
(pcm->access == SND_PCM_ACCESS_MMAP_INTERLEAVED ||
|
|
|
|
|
pcm->access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED ||
|
|
|
|
|
pcm->access == SND_PCM_ACCESS_MMAP_COMPLEX))) {
|
|
|
|
|
int err;
|
|
|
|
|
err = snd_pcm_mmap(pcm);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-26 12:16:18 +00:00
|
|
|
int snd_pcm_hw_info_strategy1(snd_pcm_t *pcm, snd_pcm_hw_info_t *info,
|
|
|
|
|
const snd_pcm_strategy_t *strategy,
|
2000-11-29 08:32:36 +00:00
|
|
|
unsigned int badness_min, unsigned int badness_max)
|
2000-11-26 12:16:18 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_hw_info_t best_info;
|
|
|
|
|
int param;
|
2000-12-07 15:58:03 +00:00
|
|
|
int value;
|
2000-11-26 12:16:18 +00:00
|
|
|
unsigned int best_badness;
|
2000-11-29 08:32:36 +00:00
|
|
|
unsigned int mask = ~0;
|
|
|
|
|
int badness = strategy->min_badness(info, badness_max, pcm, strategy);
|
|
|
|
|
snd_pcm_hw_info_t info1;
|
2000-11-26 12:16:18 +00:00
|
|
|
#if 0
|
|
|
|
|
printf("\nBadness: %d\n", badness);
|
|
|
|
|
snd_pcm_dump_hw_info(info, stdout);
|
|
|
|
|
#endif
|
|
|
|
|
if (badness < 0)
|
2000-11-29 08:32:36 +00:00
|
|
|
return badness;
|
|
|
|
|
if ((unsigned int)badness > badness_min)
|
|
|
|
|
badness_min = badness_min;
|
2000-11-26 12:16:18 +00:00
|
|
|
param = strategy->choose_param(info, pcm, strategy);
|
|
|
|
|
if (param < 0)
|
|
|
|
|
return badness;
|
|
|
|
|
best_badness = UINT_MAX;
|
|
|
|
|
value = -1;
|
|
|
|
|
while (1) {
|
|
|
|
|
int err;
|
|
|
|
|
value = strategy->next_value(info, param, value, pcm, strategy);
|
|
|
|
|
if (value < 0)
|
|
|
|
|
break;
|
|
|
|
|
info1 = *info;
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_par_set_minmax(&info1, param, value, value);
|
2000-11-26 12:16:18 +00:00
|
|
|
err = snd_pcm_hw_info(pcm, &info1);
|
2000-11-29 08:32:36 +00:00
|
|
|
if (err >= 0) {
|
|
|
|
|
badness = snd_pcm_hw_info_strategy1(pcm, &info1, strategy, badness_min, badness_max);
|
|
|
|
|
if (badness >= 0) {
|
|
|
|
|
|
|
|
|
|
if ((unsigned int) badness <= badness_min) {
|
|
|
|
|
*info = info1;
|
|
|
|
|
return badness;
|
|
|
|
|
}
|
|
|
|
|
best_badness = badness;
|
|
|
|
|
best_info = info1;
|
|
|
|
|
badness_max = badness - 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (badness != -EINVAL)
|
|
|
|
|
continue;
|
2000-11-26 12:16:18 +00:00
|
|
|
}
|
2000-11-29 08:32:36 +00:00
|
|
|
mask &= snd_pcm_hw_info_fail_mask(&info1);
|
2000-11-26 12:16:18 +00:00
|
|
|
}
|
2000-11-29 08:32:36 +00:00
|
|
|
if (best_badness == UINT_MAX) {
|
2000-12-07 15:58:03 +00:00
|
|
|
for (param = 0; param <= SND_PCM_HW_INFO_LAST; param++) {
|
2000-11-29 08:32:36 +00:00
|
|
|
if (!(mask & (1 << param)))
|
|
|
|
|
continue;
|
|
|
|
|
snd_pcm_hw_info_par_copy(info, param, &info1);
|
|
|
|
|
}
|
2000-11-26 12:16:18 +00:00
|
|
|
return -EINVAL;
|
2000-11-29 08:32:36 +00:00
|
|
|
}
|
2000-11-26 12:16:18 +00:00
|
|
|
*info = best_info;
|
|
|
|
|
return best_badness;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_hw_info_strategy(snd_pcm_t *pcm, snd_pcm_hw_info_t *info,
|
2000-11-29 08:32:36 +00:00
|
|
|
const snd_pcm_strategy_t *strategy)
|
2000-11-26 12:16:18 +00:00
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
err = snd_pcm_hw_info(pcm, info);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2000-11-29 08:32:36 +00:00
|
|
|
return snd_pcm_hw_info_strategy1(pcm, info, strategy,
|
|
|
|
|
strategy->badness_min,
|
|
|
|
|
strategy->badness_max);
|
2000-11-26 12:16:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void snd_pcm_strategy_simple_free(snd_pcm_strategy_t *strategy)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_strategy_simple_t *pars = strategy->private;
|
|
|
|
|
int k;
|
2000-12-07 15:58:03 +00:00
|
|
|
for (k = 0; k <= SND_PCM_HW_INFO_LAST; ++k) {
|
2000-11-26 12:16:18 +00:00
|
|
|
if (pars[k].valid && pars[k].free)
|
|
|
|
|
pars[k].free(&pars[k]);
|
|
|
|
|
}
|
|
|
|
|
free(pars);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_strategy_simple_choose_param(const snd_pcm_hw_info_t *info,
|
|
|
|
|
snd_pcm_t *pcm ATTRIBUTE_UNUSED,
|
|
|
|
|
const snd_pcm_strategy_t *strategy)
|
|
|
|
|
{
|
|
|
|
|
unsigned int param;
|
|
|
|
|
int best_param = -1;
|
|
|
|
|
const snd_pcm_strategy_simple_t *pars = strategy->private;
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int min_choices = UINT_MAX;
|
2000-11-29 08:32:36 +00:00
|
|
|
unsigned int min_order = UINT_MAX;
|
2000-12-07 15:58:03 +00:00
|
|
|
for (param = 0; param <= SND_PCM_HW_INFO_LAST; ++param) {
|
2000-11-29 08:32:36 +00:00
|
|
|
const snd_pcm_strategy_simple_t *p = &pars[param];
|
2000-11-26 12:16:18 +00:00
|
|
|
unsigned int choices;
|
2000-11-29 08:32:36 +00:00
|
|
|
if (!p->valid)
|
2000-11-26 12:16:18 +00:00
|
|
|
continue;
|
2000-11-29 08:32:36 +00:00
|
|
|
choices = snd_pcm_hw_info_par_choices(info, param);
|
2000-11-26 12:16:18 +00:00
|
|
|
if (choices == 1)
|
|
|
|
|
continue;
|
|
|
|
|
assert(choices != 0);
|
2000-11-29 08:32:36 +00:00
|
|
|
if (p->order < min_order ||
|
|
|
|
|
(p->order == min_order &&
|
|
|
|
|
choices < min_choices)) {
|
|
|
|
|
min_order = p->order;
|
2000-11-26 12:16:18 +00:00
|
|
|
min_choices = choices;
|
|
|
|
|
best_param = param;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return best_param;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-07 15:58:03 +00:00
|
|
|
int snd_pcm_strategy_simple_next_value(const snd_pcm_hw_info_t *info,
|
2000-11-26 12:16:18 +00:00
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
int value,
|
2000-11-26 12:16:18 +00:00
|
|
|
snd_pcm_t *pcm,
|
|
|
|
|
const snd_pcm_strategy_t *strategy)
|
|
|
|
|
{
|
|
|
|
|
const snd_pcm_strategy_simple_t *pars = strategy->private;
|
|
|
|
|
assert(pars[param].valid);
|
|
|
|
|
return pars[param].next_value(info, param, value, pcm, &pars[param]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int snd_pcm_strategy_simple_min_badness(const snd_pcm_hw_info_t *info,
|
|
|
|
|
unsigned int max_badness,
|
|
|
|
|
snd_pcm_t *pcm,
|
|
|
|
|
const snd_pcm_strategy_t *strategy)
|
|
|
|
|
{
|
|
|
|
|
unsigned int param;
|
|
|
|
|
unsigned int badness = 0;
|
|
|
|
|
const snd_pcm_strategy_simple_t *pars = strategy->private;
|
2000-12-07 15:58:03 +00:00
|
|
|
for (param = 0; param <= SND_PCM_HW_INFO_LAST; ++param) {
|
2000-11-26 12:16:18 +00:00
|
|
|
unsigned int b;
|
|
|
|
|
if (!pars[param].valid)
|
|
|
|
|
continue;
|
|
|
|
|
b = pars[param].min_badness(info, param, pcm, &pars[param]);
|
|
|
|
|
if (b > max_badness || max_badness - b < badness)
|
2000-11-29 08:32:36 +00:00
|
|
|
return -E2BIG;
|
2000-11-26 12:16:18 +00:00
|
|
|
badness += b;
|
|
|
|
|
}
|
|
|
|
|
return badness;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void snd_pcm_strategy_simple_near_free(snd_pcm_strategy_simple_t *par)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_strategy_simple_near_t *p = par->private;
|
|
|
|
|
free(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int snd_pcm_strategy_simple_near_min_badness(const snd_pcm_hw_info_t *info,
|
|
|
|
|
unsigned int param,
|
|
|
|
|
snd_pcm_t *pcm,
|
|
|
|
|
const snd_pcm_strategy_simple_t *par)
|
|
|
|
|
{
|
|
|
|
|
const snd_pcm_strategy_simple_near_t *p = par->private;
|
2000-12-07 15:58:03 +00:00
|
|
|
int value = snd_pcm_hw_info_par_nearest_next(info, param, p->best, -1, pcm);
|
|
|
|
|
int diff;
|
2000-11-26 12:16:18 +00:00
|
|
|
assert(value >= 0);
|
|
|
|
|
diff = p->best - value;
|
|
|
|
|
if (diff < 0)
|
|
|
|
|
diff = -diff;
|
|
|
|
|
return diff * p->mul;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-07 15:58:03 +00:00
|
|
|
int snd_pcm_strategy_simple_near_next_value(const snd_pcm_hw_info_t *info,
|
2000-11-26 12:16:18 +00:00
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
int value,
|
2000-11-26 12:16:18 +00:00
|
|
|
snd_pcm_t *pcm,
|
|
|
|
|
const snd_pcm_strategy_simple_t *par)
|
|
|
|
|
{
|
|
|
|
|
const snd_pcm_strategy_simple_near_t *p = par->private;
|
2000-11-29 08:32:36 +00:00
|
|
|
return snd_pcm_hw_info_par_nearest_next(info, param, p->best, value, pcm);
|
2000-11-26 12:16:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void snd_pcm_strategy_simple_choices_free(snd_pcm_strategy_simple_t *par)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_strategy_simple_choices_t *p = par->private;
|
|
|
|
|
// free(p->choices);
|
|
|
|
|
free(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int snd_pcm_strategy_simple_choices_min_badness(const snd_pcm_hw_info_t *info,
|
|
|
|
|
unsigned int param,
|
|
|
|
|
snd_pcm_t *pcm ATTRIBUTE_UNUSED,
|
|
|
|
|
const snd_pcm_strategy_simple_t *par)
|
|
|
|
|
{
|
|
|
|
|
const snd_pcm_strategy_simple_choices_t *p = par->private;
|
|
|
|
|
unsigned int k;
|
|
|
|
|
for (k = 0; k < p->count; ++k) {
|
2000-11-29 08:32:36 +00:00
|
|
|
if (snd_pcm_hw_info_par_check(info, param, p->choices[k].value))
|
2000-11-26 12:16:18 +00:00
|
|
|
return p->choices[k].badness;
|
|
|
|
|
}
|
|
|
|
|
assert(0);
|
|
|
|
|
return UINT_MAX;
|
|
|
|
|
}
|
|
|
|
|
|
2000-12-07 15:58:03 +00:00
|
|
|
int snd_pcm_strategy_simple_choices_next_value(const snd_pcm_hw_info_t *info,
|
2000-11-26 12:16:18 +00:00
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
int value,
|
2000-11-26 12:16:18 +00:00
|
|
|
snd_pcm_t *pcm ATTRIBUTE_UNUSED,
|
|
|
|
|
const snd_pcm_strategy_simple_t *par)
|
|
|
|
|
{
|
|
|
|
|
const snd_pcm_strategy_simple_choices_t *p = par->private;
|
|
|
|
|
unsigned int k = 0;
|
|
|
|
|
if (value >= 0) {
|
|
|
|
|
for (; k < p->count; ++k) {
|
2000-12-07 15:58:03 +00:00
|
|
|
if (p->choices[k].value == (unsigned int) value) {
|
2000-11-26 12:16:18 +00:00
|
|
|
k++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (; k < p->count; ++k) {
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int v = p->choices[k].value;
|
2000-11-29 08:32:36 +00:00
|
|
|
if (snd_pcm_hw_info_par_check(info, param, v))
|
2000-11-26 12:16:18 +00:00
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_strategy_free(snd_pcm_strategy_t *strategy)
|
|
|
|
|
{
|
|
|
|
|
if (strategy->free)
|
|
|
|
|
strategy->free(strategy);
|
|
|
|
|
free(strategy);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
int snd_pcm_strategy_simple(snd_pcm_strategy_t **strategyp,
|
|
|
|
|
unsigned int badness_min,
|
|
|
|
|
unsigned int badness_max)
|
2000-11-26 12:16:18 +00:00
|
|
|
{
|
|
|
|
|
snd_pcm_strategy_simple_t *data;
|
|
|
|
|
snd_pcm_strategy_t *s;
|
|
|
|
|
assert(strategyp);
|
2000-12-07 15:58:03 +00:00
|
|
|
data = calloc(SND_PCM_HW_INFO_LAST + 1, sizeof(*data));
|
2000-11-26 12:16:18 +00:00
|
|
|
if (!data)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
s = calloc(1, sizeof(*s));
|
|
|
|
|
if (!s) {
|
|
|
|
|
free(data);
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
}
|
|
|
|
|
s->choose_param = snd_pcm_strategy_simple_choose_param;
|
|
|
|
|
s->next_value = snd_pcm_strategy_simple_next_value;
|
|
|
|
|
s->min_badness = snd_pcm_strategy_simple_min_badness;
|
2000-11-29 08:32:36 +00:00
|
|
|
s->badness_min = badness_min;
|
|
|
|
|
s->badness_max = badness_max;
|
2000-11-26 12:16:18 +00:00
|
|
|
s->private = data;
|
|
|
|
|
s->free = snd_pcm_strategy_simple_free;
|
|
|
|
|
*strategyp = s;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_strategy_simple_near(snd_pcm_strategy_t *strategy,
|
2000-11-29 08:32:36 +00:00
|
|
|
int order,
|
2000-11-26 12:16:18 +00:00
|
|
|
unsigned int param,
|
2000-12-07 15:58:03 +00:00
|
|
|
unsigned int best,
|
2000-11-26 12:16:18 +00:00
|
|
|
unsigned int mul)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_strategy_simple_t *s = strategy->private;
|
|
|
|
|
snd_pcm_strategy_simple_near_t *data;
|
|
|
|
|
assert(strategy);
|
2000-12-07 15:58:03 +00:00
|
|
|
assert(param <= SND_PCM_HW_INFO_LAST);
|
2000-11-26 12:16:18 +00:00
|
|
|
assert(!s->valid);
|
|
|
|
|
data = calloc(1, sizeof(*data));
|
|
|
|
|
if (!data)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
data->best = best;
|
|
|
|
|
data->mul = mul;
|
|
|
|
|
s += param;
|
2000-11-29 08:32:36 +00:00
|
|
|
s->order = order;
|
2000-11-26 12:16:18 +00:00
|
|
|
s->valid = 1;
|
|
|
|
|
s->next_value = snd_pcm_strategy_simple_near_next_value;
|
|
|
|
|
s->min_badness = snd_pcm_strategy_simple_near_min_badness;
|
|
|
|
|
s->private = data;
|
|
|
|
|
s->free = snd_pcm_strategy_simple_near_free;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_strategy_simple_choices(snd_pcm_strategy_t *strategy,
|
2000-11-29 08:32:36 +00:00
|
|
|
int order,
|
2000-11-26 12:16:18 +00:00
|
|
|
unsigned int param,
|
|
|
|
|
unsigned int count,
|
|
|
|
|
snd_pcm_strategy_simple_choices_list_t *choices)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_strategy_simple_t *s = strategy->private;
|
|
|
|
|
snd_pcm_strategy_simple_choices_t *data;
|
|
|
|
|
assert(strategy);
|
2000-12-07 15:58:03 +00:00
|
|
|
assert(param <= SND_PCM_HW_INFO_LAST);
|
2000-11-26 12:16:18 +00:00
|
|
|
assert(!s->valid);
|
|
|
|
|
data = calloc(1, sizeof(*data));
|
|
|
|
|
if (!data)
|
|
|
|
|
return -ENOMEM;
|
|
|
|
|
data->count = count;
|
|
|
|
|
data->choices = choices;
|
|
|
|
|
s += param;
|
|
|
|
|
s->valid = 1;
|
2000-11-29 08:32:36 +00:00
|
|
|
s->order = order;
|
2000-11-26 12:16:18 +00:00
|
|
|
s->next_value = snd_pcm_strategy_simple_choices_next_value;
|
|
|
|
|
s->min_badness = snd_pcm_strategy_simple_choices_min_badness;
|
|
|
|
|
s->private = data;
|
|
|
|
|
s->free = snd_pcm_strategy_simple_choices_free;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-29 08:32:36 +00:00
|
|
|
int snd_pcm_dump_hw_info(snd_pcm_hw_info_t *info, FILE *fp)
|
|
|
|
|
{
|
|
|
|
|
unsigned int param;
|
2000-12-07 15:58:03 +00:00
|
|
|
for (param = 0; param <= SND_PCM_HW_INFO_LAST; param++) {
|
|
|
|
|
fprintf(fp, "%s: ", snd_pcm_hw_info_names[param]);
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_par_dump(info, param, fp);
|
|
|
|
|
putc('\n', fp);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_hw_info_try_explain_failure1(snd_pcm_t *pcm,
|
|
|
|
|
snd_pcm_hw_info_t *fail,
|
|
|
|
|
snd_pcm_hw_info_t *success,
|
|
|
|
|
unsigned int depth,
|
|
|
|
|
FILE *fp)
|
|
|
|
|
{
|
|
|
|
|
unsigned int param;
|
|
|
|
|
snd_pcm_hw_info_t i;
|
|
|
|
|
if (depth < 1)
|
|
|
|
|
return -ENOENT;
|
2000-12-07 15:58:03 +00:00
|
|
|
for (param = 0; param <= SND_PCM_HW_INFO_LAST; param++) {
|
2000-11-29 08:32:36 +00:00
|
|
|
int err;
|
|
|
|
|
i = *success;
|
|
|
|
|
snd_pcm_hw_info_par_copy(&i, param, fail);
|
|
|
|
|
err = snd_pcm_hw_info(pcm, &i);
|
|
|
|
|
if (err == 0 &&
|
|
|
|
|
snd_pcm_hw_info_try_explain_failure1(pcm, fail, &i, depth - 1, fp) < 0)
|
|
|
|
|
continue;
|
2000-12-07 15:58:03 +00:00
|
|
|
fprintf(fp, "%s: ", snd_pcm_hw_info_names[param]);
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_par_dump(fail, param, fp);
|
|
|
|
|
putc('\n', fp);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return -ENOENT;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int snd_pcm_hw_info_try_explain_failure(snd_pcm_t *pcm,
|
|
|
|
|
snd_pcm_hw_info_t *fail,
|
|
|
|
|
snd_pcm_hw_info_t *success,
|
|
|
|
|
unsigned int depth,
|
|
|
|
|
FILE *fp)
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_hw_info_t i, any;
|
|
|
|
|
int err;
|
|
|
|
|
unsigned int fail_mask;
|
|
|
|
|
assert(pcm && fail);
|
|
|
|
|
fail_mask = snd_pcm_hw_info_fail_mask(fail);
|
|
|
|
|
if (fail_mask) {
|
|
|
|
|
unsigned int param;
|
2000-12-07 15:58:03 +00:00
|
|
|
for (param = 0; param <= SND_PCM_HW_INFO_LAST; param++) {
|
2000-11-29 08:32:36 +00:00
|
|
|
if (!(fail_mask & (1 << param)))
|
|
|
|
|
continue;
|
2000-12-07 15:58:03 +00:00
|
|
|
fprintf(fp, "%s: ", snd_pcm_hw_info_names[param]);
|
2000-11-29 08:32:36 +00:00
|
|
|
snd_pcm_hw_info_par_dump(fail, param, fp);
|
|
|
|
|
putc('\n', fp);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
i = *fail;
|
|
|
|
|
err = snd_pcm_hw_info(pcm, &i);
|
|
|
|
|
if (err == 0) {
|
|
|
|
|
fprintf(fp, "Too low max badness or configuration temporarily unavailable\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (!success) {
|
|
|
|
|
snd_pcm_hw_info_any(&any);
|
|
|
|
|
success = &any;
|
|
|
|
|
}
|
|
|
|
|
return snd_pcm_hw_info_try_explain_failure1(pcm, fail, success, depth, fp);
|
|
|
|
|
}
|
|
|
|
|
|
2000-11-22 14:27:37 +00:00
|
|
|
size_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
return *pcm->hw_ptr;
|
|
|
|
|
}
|
2000-11-26 12:16:18 +00:00
|
|
|
|