2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \file pcm.c
|
|
|
|
|
* \author Jaroslav Kysela <perex@suse.cz>
|
|
|
|
|
* \author Abramo Bagnara <abramo@alsa-project.org>
|
|
|
|
|
* \date 2000-2001
|
|
|
|
|
*
|
|
|
|
|
* PCM Interface is designed to write or read digital audio frames. A
|
|
|
|
|
* frame is the data unit converted into/from sound in one time unit
|
|
|
|
|
* (1/rate seconds), by example if you set your playback PCM rate to
|
|
|
|
|
* 44100 you'll hear 44100 frames per second. The size in bytes of a
|
|
|
|
|
* frame may be obtained from bits needed to store a sample and
|
|
|
|
|
* channels count.
|
|
|
|
|
*/
|
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>
|
2000-10-11 12:37:27 +00:00
|
|
|
#include <stdarg.h>
|
2001-03-17 16:34:43 +00:00
|
|
|
#include <signal.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
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get identifier of PCM handle
|
|
|
|
|
* \param pcm a PCM handle
|
|
|
|
|
* \return ascii identifier of PCM handle
|
|
|
|
|
*
|
|
|
|
|
* Returns the ASCII identifier of given PCM handle. It's the same
|
|
|
|
|
* identifier as for snd_pcm_open().
|
|
|
|
|
*/
|
2001-02-11 15:45:35 +00:00
|
|
|
const char *snd_pcm_name(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
assert(pcm);
|
|
|
|
|
return pcm->name;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get type of PCM handle
|
|
|
|
|
* \param pcm a PCM handle
|
|
|
|
|
* \return type of PCM handle
|
|
|
|
|
*
|
|
|
|
|
* Returns the type #snd_pcm_type_t of given PCM handle.
|
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get stream for a PCM handle
|
|
|
|
|
* \param pcm a PCM handle
|
|
|
|
|
* \return stream of PCM handle
|
|
|
|
|
*
|
|
|
|
|
* Returns the type #snd_pcm_stream_t of given PCM handle.
|
|
|
|
|
*/
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_stream_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
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief close PCM handle
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*
|
|
|
|
|
* Closes the specified PCM handle and frees all associated
|
|
|
|
|
* resources.
|
|
|
|
|
*/
|
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) {
|
2001-03-07 13:56:13 +00:00
|
|
|
if (pcm->mode & SND_PCM_NONBLOCK ||
|
|
|
|
|
pcm->stream == SND_PCM_STREAM_CAPTURE)
|
2000-10-15 14:15:30 +00:00
|
|
|
snd_pcm_drop(pcm);
|
|
|
|
|
else
|
|
|
|
|
snd_pcm_drain(pcm);
|
2001-01-19 13:10:50 +00:00
|
|
|
err = snd_pcm_hw_free(pcm);
|
|
|
|
|
if (err < 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
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief set nonblock mode
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param nonblock 0 = block, 1 = nonblock mode
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief set async mode
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param sig Signal to raise: < 0 disable, 0 default (SIGIO)
|
|
|
|
|
* \param pid Process ID to signal: 0 current
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*
|
|
|
|
|
* A signal is raised every period.
|
|
|
|
|
*/
|
2000-10-11 12:37:27 +00:00
|
|
|
int snd_pcm_async(snd_pcm_t *pcm, int sig, pid_t pid)
|
|
|
|
|
{
|
2001-03-17 16:34:43 +00:00
|
|
|
int err;
|
2000-10-11 12:37:27 +00:00
|
|
|
assert(pcm);
|
2001-03-17 16:34:43 +00:00
|
|
|
err = pcm->ops->async(pcm->op_arg, sig, pid);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
if (sig)
|
|
|
|
|
pcm->async_sig = sig;
|
|
|
|
|
else
|
|
|
|
|
pcm->async_sig = SIGIO;
|
|
|
|
|
if (pid)
|
|
|
|
|
pcm->async_pid = pid;
|
|
|
|
|
else
|
|
|
|
|
pcm->async_pid = getpid();
|
|
|
|
|
return 0;
|
2000-10-11 12:37:27 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Obtain general (static) information for PCM handle
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param info Information container
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/** \brief Install one PCM hardware configuration choosen from a configuration space and #snd_pcm_prepare it
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param params Configuration space definition container
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*
|
|
|
|
|
* The configuration is choosen fixing single parameters in this order:
|
|
|
|
|
* first access, first format, first subformat, min channels, min rate,
|
|
|
|
|
* min period time, max buffer size, min tick time
|
|
|
|
|
*/
|
|
|
|
|
int snd_pcm_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
assert(pcm && params);
|
|
|
|
|
err = _snd_pcm_hw_params(pcm, params);
|
|
|
|
|
if (err >= 0)
|
|
|
|
|
err = snd_pcm_prepare(pcm);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \brief Remove PCM hardware configuration and free associated resources
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
int snd_pcm_hw_free(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
assert(pcm->setup);
|
|
|
|
|
assert(snd_pcm_state(pcm) <= SND_PCM_STATE_PREPARED);
|
|
|
|
|
if (pcm->mmap_channels) {
|
|
|
|
|
err = snd_pcm_munmap(pcm);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
err = pcm->ops->hw_free(pcm->op_arg);
|
|
|
|
|
pcm->setup = 0;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** \brief Install PCM software configuration defined by params
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param params Configuration container
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
|
|
|
|
int snd_pcm_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)
|
|
|
|
|
{
|
|
|
|
|
int err;
|
|
|
|
|
err = pcm->ops->sw_params(pcm->op_arg, params);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
pcm->start_mode = snd_pcm_sw_params_get_start_mode(params);
|
|
|
|
|
pcm->xrun_mode = snd_pcm_sw_params_get_xrun_mode(params);
|
|
|
|
|
pcm->tstamp_mode = snd_pcm_sw_params_get_tstamp_mode(params);
|
|
|
|
|
pcm->period_step = params->period_step;
|
|
|
|
|
pcm->sleep_min = params->sleep_min;
|
|
|
|
|
pcm->avail_min = params->avail_min;
|
|
|
|
|
pcm->xfer_align = params->xfer_align;
|
|
|
|
|
pcm->silence_threshold = params->silence_threshold;
|
|
|
|
|
pcm->silence_size = params->silence_size;
|
|
|
|
|
pcm->boundary = params->boundary;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief Obtain status (runtime) information for PCM handle
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param status Status container
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Return PCM state
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \return PCM state #snd_pcm_state_t of given PCM handle
|
|
|
|
|
*/
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_state_t 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
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Obtain delay in frames for a running PCM handle
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param delayp Returned delay
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*
|
|
|
|
|
* Delay is distance between current application frame position and
|
|
|
|
|
* sound frame position.
|
|
|
|
|
* It's positive and less than buffer size in normal situation,
|
|
|
|
|
* negative on playback underrun and greater than buffer size on
|
|
|
|
|
* capture overrun.
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
int snd_pcm_delay(snd_pcm_t *pcm, snd_pcm_sframes_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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Prepare PCM for use
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Reset PCM position
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*
|
|
|
|
|
* Reduce PCM delay to 0.
|
|
|
|
|
*/
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Start a PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Stop a PCM dropping pending frames
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Stop a PCM preserving pending frames
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*
|
|
|
|
|
* For playback wait for all pending frames to be played and then stop
|
|
|
|
|
* the PCM.
|
|
|
|
|
* For capture stop PCM permitting to retrieve residual frames.
|
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Pause/resume PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param pause 0 = resume, 1 = pause
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Move application frame position backward
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param frames wanted displacement in frames
|
|
|
|
|
* \return a positive number for actual displacement otherwise a
|
|
|
|
|
* negative error code
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_sframes_t snd_pcm_rewind(snd_pcm_t *pcm, snd_pcm_uframes_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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Write interleaved frames to a PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param buffer frames containing buffer
|
|
|
|
|
* \param size frames to be written
|
|
|
|
|
* \return a positive number of frames actually written otherwise a
|
|
|
|
|
* negative error code
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_sframes_t snd_pcm_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Write non interleaved frames to a PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param bufs frames containing buffers (one for each channel)
|
|
|
|
|
* \param size frames to be written
|
|
|
|
|
* \return a positive number of frames actually written otherwise a
|
|
|
|
|
* negative error code
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_sframes_t snd_pcm_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Read interleaved frames from a PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param buffer frames containing buffer
|
|
|
|
|
* \param size frames to be written
|
|
|
|
|
* \return a positive number of frames actually read otherwise a
|
|
|
|
|
* negative error code
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_sframes_t snd_pcm_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_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
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Read non interleaved frames to a PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param bufs frames containing buffers (one for each channel)
|
|
|
|
|
* \param size frames to be written
|
|
|
|
|
* \return a positive number of frames actually read otherwise a
|
|
|
|
|
* negative error code
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_sframes_t snd_pcm_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_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
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Link two PCMs
|
|
|
|
|
* \param pcm1 first PCM handle
|
|
|
|
|
* \param pcm2 first PCM handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*
|
|
|
|
|
* The two PCMs will start/stop/prepare in sync.
|
|
|
|
|
*/
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_link(snd_pcm_t *pcm1, snd_pcm_t *pcm2)
|
|
|
|
|
{
|
2001-02-12 23:51:49 +00:00
|
|
|
int fd1 = _snd_pcm_link_descriptor(pcm1);
|
|
|
|
|
int fd2 = _snd_pcm_link_descriptor(pcm2);
|
2000-09-24 09:57:26 +00:00
|
|
|
if (fd1 < 0 || fd2 < 0)
|
|
|
|
|
return -ENOSYS;
|
2001-01-29 14:27:53 +00:00
|
|
|
if (ioctl(fd1, SNDRV_PCM_IOCTL_LINK, fd2) < 0) {
|
|
|
|
|
SYSERR("SNDRV_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;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Remove a PCM from a linked group
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
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;
|
2001-02-12 23:51:49 +00:00
|
|
|
fd = _snd_pcm_link_descriptor(pcm);
|
2001-01-29 14:27:53 +00:00
|
|
|
if (ioctl(fd, SNDRV_PCM_IOCTL_UNLINK) < 0) {
|
|
|
|
|
SYSERR("SNDRV_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;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get count of poll descriptors for PCM handle
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \return count of poll descriptors
|
|
|
|
|
*/
|
|
|
|
|
int snd_pcm_poll_descriptors_count(snd_pcm_t *pcm)
|
2000-05-08 18:53:38 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2001-03-23 11:05:41 +00:00
|
|
|
return 1;
|
2000-01-31 12:40:05 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* \brief get poll descriptors
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param pfds array of poll descriptors
|
|
|
|
|
* \param space space in the poll descriptor array
|
|
|
|
|
* \return count of filled descriptors
|
|
|
|
|
*/
|
2001-02-12 23:51:49 +00:00
|
|
|
int snd_pcm_poll_descriptors(snd_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
|
|
|
|
|
{
|
|
|
|
|
assert(pcm);
|
|
|
|
|
if (space >= 1) {
|
|
|
|
|
pfds->fd = pcm->poll_fd;
|
|
|
|
|
pfds->events = pcm->stream == SND_PCM_STREAM_PLAYBACK ? POLLOUT : POLLIN;
|
|
|
|
|
}
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
#ifndef DOC_HIDDEN
|
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
|
2000-12-29 15:00:53 +00:00
|
|
|
#define SILENCE(v) [SND_PCM_SILENCE_##v] = #v
|
|
|
|
|
#define TSTAMP(v) [SND_PCM_TSTAMP_##v] = #v
|
2000-11-29 08:32:36 +00:00
|
|
|
#define ACCESS(v) [SND_PCM_ACCESS_##v] = #v
|
|
|
|
|
#define START(v) [SND_PCM_START_##v] = #v
|
|
|
|
|
#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
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
static const char *snd_pcm_stream_names[] = {
|
2000-11-20 20:10:46 +00:00
|
|
|
STREAM(PLAYBACK),
|
|
|
|
|
STREAM(CAPTURE),
|
|
|
|
|
};
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
static const 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),
|
|
|
|
|
};
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
static const 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),
|
|
|
|
|
};
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
static const char *snd_pcm_format_names[] = {
|
2000-11-29 08:32:36 +00:00
|
|
|
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),
|
|
|
|
|
};
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
static const 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 32 bit Little Endian"),
|
|
|
|
|
FORMATD(FLOAT_BE, "Float 32 bit Big Endian"),
|
|
|
|
|
FORMATD(FLOAT64_LE, "Float 64 bit Little Endian"),
|
|
|
|
|
FORMATD(FLOAT64_BE, "Float 64 bit Big Endian"),
|
2000-11-29 08:32:36 +00:00
|
|
|
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"),
|
|
|
|
|
};
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
static const char *snd_pcm_subformat_names[] = {
|
2000-11-29 08:32:36 +00:00
|
|
|
SUBFORMAT(STD),
|
2000-05-25 08:36:58 +00:00
|
|
|
};
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
static const char *snd_pcm_subformat_descriptions[] = {
|
2000-11-29 08:32:36 +00:00
|
|
|
SUBFORMATD(STD, "Standard"),
|
2000-11-20 20:10:46 +00:00
|
|
|
};
|
2000-05-25 08:36:58 +00:00
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
static const 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
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
static const char *snd_pcm_xrun_mode_names[] = {
|
2000-11-20 20:10:46 +00:00
|
|
|
XRUN(NONE),
|
2001-01-15 11:06:53 +00:00
|
|
|
XRUN(STOP),
|
2000-11-20 20:10:46 +00:00
|
|
|
};
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
static const char *snd_pcm_tstamp_mode_names[] = {
|
2000-12-29 15:00:53 +00:00
|
|
|
TSTAMP(NONE),
|
|
|
|
|
TSTAMP(MMAP),
|
|
|
|
|
};
|
2001-03-23 11:05:41 +00:00
|
|
|
#endif
|
2000-12-29 15:00:53 +00:00
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get name of PCM stream
|
|
|
|
|
* \param stream PCM stream
|
|
|
|
|
* \return ascii name of PCM stream
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
const char *snd_pcm_stream_name(snd_pcm_stream_t stream)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
|
|
|
|
assert(stream <= SND_PCM_STREAM_LAST);
|
2001-02-04 17:03:17 +00:00
|
|
|
return snd_pcm_stream_names[snd_enum_to_int(stream)];
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get name of PCM access type
|
|
|
|
|
* \param access PCM access type
|
|
|
|
|
* \return ascii name of PCM access type
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
const char *snd_pcm_access_name(snd_pcm_access_t access)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
|
|
|
|
assert(access <= SND_PCM_ACCESS_LAST);
|
2001-02-04 17:03:17 +00:00
|
|
|
return snd_pcm_access_names[snd_enum_to_int(access)];
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get name of PCM sample format
|
|
|
|
|
* \param format PCM sample format
|
|
|
|
|
* \return ascii name of PCM sample format
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
const char *snd_pcm_format_name(snd_pcm_format_t format)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
|
|
|
|
assert(format <= SND_PCM_FORMAT_LAST);
|
2001-02-04 17:03:17 +00:00
|
|
|
return snd_pcm_format_names[snd_enum_to_int(format)];
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get description of PCM sample format
|
|
|
|
|
* \param format PCM sample format
|
|
|
|
|
* \return ascii description of PCM sample format
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
const char *snd_pcm_format_description(snd_pcm_format_t format)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
|
|
|
|
assert(format <= SND_PCM_FORMAT_LAST);
|
2001-02-04 17:03:17 +00:00
|
|
|
return snd_pcm_format_descriptions[snd_enum_to_int(format)];
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get PCM sample format from name
|
|
|
|
|
* \param name PCM sample format name (case insensitive)
|
|
|
|
|
* \return PCM sample format
|
|
|
|
|
*/
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_format_t snd_pcm_format_value(const char* name)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_format_t format;
|
|
|
|
|
for (format = 0; format <= SND_PCM_FORMAT_LAST; snd_enum_incr(format)) {
|
|
|
|
|
if (snd_pcm_format_names[snd_enum_to_int(format)] &&
|
|
|
|
|
strcasecmp(name, snd_pcm_format_names[snd_enum_to_int(format)]) == 0) {
|
2000-12-29 15:00:53 +00:00
|
|
|
return format;
|
2001-02-04 17:03:17 +00:00
|
|
|
}
|
|
|
|
|
}
|
2001-02-05 15:44:42 +00:00
|
|
|
return SND_PCM_FORMAT_UNKNOWN;
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get name of PCM sample subformat
|
|
|
|
|
* \param format PCM sample subformat
|
|
|
|
|
* \return ascii name of PCM sample subformat
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
const char *snd_pcm_subformat_name(snd_pcm_subformat_t subformat)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
|
|
|
|
assert(subformat <= SND_PCM_SUBFORMAT_LAST);
|
2001-02-04 17:03:17 +00:00
|
|
|
return snd_pcm_subformat_names[snd_enum_to_int(subformat)];
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get description of PCM sample subformat
|
|
|
|
|
* \param subformat PCM sample subformat
|
|
|
|
|
* \return ascii description of PCM sample subformat
|
|
|
|
|
*/
|
|
|
|
|
const char *snd_pcm_subformat_description(snd_pcm_subformat_t subformat)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
2001-03-23 11:05:41 +00:00
|
|
|
assert(subformat <= SND_PCM_SUBFORMAT_LAST);
|
|
|
|
|
return snd_pcm_subformat_descriptions[snd_enum_to_int(subformat)];
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
2000-11-29 08:32:36 +00:00
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get name of PCM start mode setting
|
|
|
|
|
* \param mode PCM start mode
|
|
|
|
|
* \return ascii name of PCM start mode setting
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
const char *snd_pcm_start_mode_name(snd_pcm_start_t mode)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
|
|
|
|
assert(mode <= SND_PCM_START_LAST);
|
2001-02-04 17:03:17 +00:00
|
|
|
return snd_pcm_start_mode_names[snd_enum_to_int(mode)];
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get name of PCM xrun mode setting
|
|
|
|
|
* \param mode PCM xrun mode
|
|
|
|
|
* \return ascii name of PCM xrun mode setting
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
const char *snd_pcm_xrun_mode_name(snd_pcm_xrun_t mode)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
|
|
|
|
assert(mode <= SND_PCM_XRUN_LAST);
|
2001-02-04 17:03:17 +00:00
|
|
|
return snd_pcm_xrun_mode_names[snd_enum_to_int(mode)];
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get name of PCM tstamp mode setting
|
|
|
|
|
* \param mode PCM tstamp mode
|
|
|
|
|
* \return ascii name of PCM tstamp mode setting
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
const char *snd_pcm_tstamp_mode_name(snd_pcm_tstamp_t mode)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
|
|
|
|
assert(mode <= SND_PCM_TSTAMP_LAST);
|
2001-02-04 17:03:17 +00:00
|
|
|
return snd_pcm_tstamp_mode_names[snd_enum_to_int(mode)];
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief get name of PCM state
|
|
|
|
|
* \param state PCM state
|
|
|
|
|
* \return ascii name of PCM state
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
const char *snd_pcm_state_name(snd_pcm_state_t state)
|
2000-12-29 15:00:53 +00:00
|
|
|
{
|
|
|
|
|
assert(state <= SND_PCM_STATE_LAST);
|
2001-02-04 17:03:17 +00:00
|
|
|
return snd_pcm_state_names[snd_enum_to_int(state)];
|
2000-12-29 15:00:53 +00:00
|
|
|
}
|
2000-11-29 08:32:36 +00:00
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Dump current hardware setup for PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param out Output handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-17 11:00:32 +00:00
|
|
|
int snd_pcm_dump_hw_setup(snd_pcm_t *pcm, snd_output_t *out)
|
2000-11-20 20:10:46 +00:00
|
|
|
{
|
|
|
|
|
assert(pcm);
|
2001-01-17 11:00:32 +00:00
|
|
|
assert(out);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_printf(out, "stream : %s\n", snd_pcm_stream_name(pcm->stream));
|
|
|
|
|
snd_output_printf(out, "access : %s\n", snd_pcm_access_name(pcm->access));
|
|
|
|
|
snd_output_printf(out, "format : %s\n", snd_pcm_format_name(pcm->format));
|
|
|
|
|
snd_output_printf(out, "subformat : %s\n", snd_pcm_subformat_name(pcm->subformat));
|
|
|
|
|
snd_output_printf(out, "channels : %u\n", pcm->channels);
|
|
|
|
|
snd_output_printf(out, "rate : %u\n", pcm->rate);
|
|
|
|
|
snd_output_printf(out, "exact rate : %g (%u/%u)\n", (double) pcm->rate_num / pcm->rate_den, pcm->rate_num, pcm->rate_den);
|
|
|
|
|
snd_output_printf(out, "msbits : %u\n", pcm->msbits);
|
|
|
|
|
snd_output_printf(out, "buffer_size : %lu\n", pcm->buffer_size);
|
|
|
|
|
snd_output_printf(out, "period_size : %lu\n", pcm->period_size);
|
|
|
|
|
snd_output_printf(out, "period_time : %u\n", pcm->period_time);
|
|
|
|
|
snd_output_printf(out, "tick_time : %u\n", pcm->tick_time);
|
2000-11-20 20:10:46 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Dump current software setup for PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param out Output handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-17 11:00:32 +00:00
|
|
|
int snd_pcm_dump_sw_setup(snd_pcm_t *pcm, snd_output_t *out)
|
2000-05-25 08:36:58 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2001-01-17 11:00:32 +00:00
|
|
|
assert(out);
|
2000-11-20 20:10:46 +00:00
|
|
|
assert(pcm->setup);
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_printf(out, "start_mode : %s\n", snd_pcm_start_mode_name(pcm->start_mode));
|
|
|
|
|
snd_output_printf(out, "xrun_mode : %s\n", snd_pcm_xrun_mode_name(pcm->xrun_mode));
|
|
|
|
|
snd_output_printf(out, "tstamp_mode : %s\n", snd_pcm_tstamp_mode_name(pcm->tstamp_mode));
|
|
|
|
|
snd_output_printf(out, "period_step : %ld\n", (long)pcm->period_step);
|
|
|
|
|
snd_output_printf(out, "sleep_min : %ld\n", (long)pcm->sleep_min);
|
|
|
|
|
snd_output_printf(out, "avail_min : %ld\n", (long)pcm->avail_min);
|
|
|
|
|
snd_output_printf(out, "xfer_align : %ld\n", (long)pcm->xfer_align);
|
|
|
|
|
snd_output_printf(out, "silence_threshold: %ld\n", (long)pcm->silence_threshold);
|
|
|
|
|
snd_output_printf(out, "silence_size : %ld\n", (long)pcm->silence_size);
|
|
|
|
|
snd_output_printf(out, "boundary : %ld\n", (long)pcm->boundary);
|
2000-11-20 20:10:46 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Dump current setup (hardware and software) for PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param out Output handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-17 11:00:32 +00:00
|
|
|
int snd_pcm_dump_setup(snd_pcm_t *pcm, snd_output_t *out)
|
2000-11-20 20:10:46 +00:00
|
|
|
{
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_pcm_dump_hw_setup(pcm, out);
|
|
|
|
|
snd_pcm_dump_sw_setup(pcm, out);
|
2000-11-20 20:10:46 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Dump status
|
|
|
|
|
* \param status Status container
|
|
|
|
|
* \param out Output handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-17 11:00:32 +00:00
|
|
|
int snd_pcm_status_dump(snd_pcm_status_t *status, snd_output_t *out)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
|
|
|
|
assert(status);
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_output_printf(out, "state : %s\n", snd_pcm_state_name((snd_pcm_state_t) status->state));
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_printf(out, "trigger_time: %ld.%06ld\n",
|
2001-01-31 17:26:56 +00:00
|
|
|
status->trigger_tstamp.tv_sec, status->trigger_tstamp.tv_usec);
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_printf(out, "tstamp : %ld.%06ld\n",
|
2000-09-24 09:57:26 +00:00
|
|
|
status->tstamp.tv_sec, status->tstamp.tv_usec);
|
2001-01-17 11:00:32 +00:00
|
|
|
snd_output_printf(out, "delay : %ld\n", (long)status->delay);
|
|
|
|
|
snd_output_printf(out, "avail : %ld\n", (long)status->avail);
|
|
|
|
|
snd_output_printf(out, "avail_max : %ld\n", (long)status->avail_max);
|
2000-05-25 08:36:58 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Dump PCM info
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param out Output handle
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-17 11:00:32 +00:00
|
|
|
int snd_pcm_dump(snd_pcm_t *pcm, snd_output_t *out)
|
2000-07-17 15:33:29 +00:00
|
|
|
{
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcm);
|
2001-01-17 11:00:32 +00:00
|
|
|
assert(out);
|
|
|
|
|
pcm->ops->dump(pcm->op_arg, out);
|
2000-07-17 15:33:29 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Convert bytes in frames for a PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param bytes quantity in bytes
|
|
|
|
|
* \return quantity expressed in frames
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_sframes_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);
|
2001-01-15 15:15:24 +00:00
|
|
|
return bytes * 8 / pcm->frame_bits;
|
2000-06-10 12:39:51 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Convert frames in bytes for a PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param frames quantity in frames
|
|
|
|
|
* \return quantity expressed in bytes
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
ssize_t snd_pcm_frames_to_bytes(snd_pcm_t *pcm, snd_pcm_sframes_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);
|
2001-01-15 15:15:24 +00:00
|
|
|
return frames * pcm->frame_bits / 8;
|
2000-06-10 12:39:51 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Convert bytes in samples for a PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param bytes quantity in bytes
|
|
|
|
|
* \return quantity expressed in samples
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
int 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);
|
2001-01-15 15:15:24 +00:00
|
|
|
return bytes * 8 / pcm->sample_bits;
|
2000-06-10 12:39:51 +00:00
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Convert samples in bytes for a PCM
|
|
|
|
|
* \param pcm PCM handle
|
|
|
|
|
* \param samples quantity in samples
|
|
|
|
|
* \return quantity expressed in bytes
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
ssize_t snd_pcm_samples_to_bytes(snd_pcm_t *pcm, int 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);
|
2001-01-15 15:15:24 +00:00
|
|
|
return samples * pcm->sample_bits / 8;
|
2000-06-10 12:39:51 +00:00
|
|
|
}
|
2000-06-21 14:59:20 +00:00
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Opens a PCM
|
|
|
|
|
* \param pcmp Returned PCM handle
|
|
|
|
|
* \param name ASCII identifier of the PCM handle
|
|
|
|
|
* \param stream Wanted stream
|
|
|
|
|
* \param mode Open mode (see #SND_PCM_NONBLOCK, #SND_PCM_ASYNC)
|
|
|
|
|
* \return a negative error code on failure or zero on success
|
|
|
|
|
*/
|
2001-02-06 23:48:10 +00:00
|
|
|
int snd_pcm_open(snd_pcm_t **pcmp, const char *name,
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_stream_t stream, int mode)
|
2000-08-24 17:07:44 +00:00
|
|
|
{
|
2001-02-06 23:48:10 +00:00
|
|
|
const char *str;
|
2001-03-07 12:36:05 +00:00
|
|
|
char buf[256];
|
2000-08-24 17:07:44 +00:00
|
|
|
int err;
|
2001-03-07 12:36:05 +00:00
|
|
|
snd_config_t *pcm_conf, *conf, *type_conf = NULL;
|
2001-02-11 15:45:35 +00:00
|
|
|
snd_config_iterator_t i, next;
|
2001-02-06 23:48:10 +00:00
|
|
|
const char *lib = NULL, *open = NULL;
|
|
|
|
|
int (*open_func)(snd_pcm_t **pcmp, const char *name, snd_config_t *conf,
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_stream_t stream, int mode);
|
2000-09-24 09:57:26 +00:00
|
|
|
void *h;
|
2001-03-17 16:34:43 +00:00
|
|
|
const char *name1;
|
2000-09-24 09:57:26 +00:00
|
|
|
assert(pcmp && name);
|
|
|
|
|
err = snd_config_update();
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-03-17 16:34:43 +00:00
|
|
|
|
|
|
|
|
err = snd_config_search_alias(snd_config, "pcm", name, &pcm_conf);
|
|
|
|
|
name1 = name;
|
|
|
|
|
if (err < 0 || snd_config_get_string(pcm_conf, &name1) >= 0) {
|
2000-11-20 20:10:46 +00:00
|
|
|
int card, dev, subdev;
|
|
|
|
|
char socket[256], sname[256];
|
|
|
|
|
char format[16], file[256];
|
2001-03-17 16:34:43 +00:00
|
|
|
err = sscanf(name1, "hw:%d,%d,%d", &card, &dev, &subdev);
|
2000-11-20 20:10:46 +00:00
|
|
|
if (err == 3)
|
|
|
|
|
return snd_pcm_hw_open(pcmp, name, card, dev, subdev, stream, mode);
|
2001-03-17 16:34:43 +00:00
|
|
|
err = sscanf(name1, "hw:%d,%d", &card, &dev);
|
2000-11-20 20:10:46 +00:00
|
|
|
if (err == 2)
|
|
|
|
|
return snd_pcm_hw_open(pcmp, name, card, dev, -1, stream, mode);
|
2001-03-17 16:34:43 +00:00
|
|
|
err = sscanf(name1, "plug:%d,%d,%d", &card, &dev, &subdev);
|
2000-11-20 20:10:46 +00:00
|
|
|
if (err == 3)
|
|
|
|
|
return snd_pcm_plug_open_hw(pcmp, name, card, dev, subdev, stream, mode);
|
2001-03-17 16:34:43 +00:00
|
|
|
err = sscanf(name1, "plug:%d,%d", &card, &dev);
|
2000-11-20 20:10:46 +00:00
|
|
|
if (err == 2)
|
|
|
|
|
return snd_pcm_plug_open_hw(pcmp, name, card, dev, -1, stream, mode);
|
2001-03-17 16:34:43 +00:00
|
|
|
err = sscanf(name1, "plug:%256[^,]", sname);
|
2001-03-01 22:46:47 +00:00
|
|
|
if (err == 1) {
|
|
|
|
|
snd_pcm_t *slave;
|
|
|
|
|
err = snd_pcm_open(&slave, sname, stream, mode);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
|
|
|
|
return snd_pcm_plug_open(pcmp, name, NULL, 0, 0, 0, slave, 1);
|
|
|
|
|
}
|
2001-03-17 16:34:43 +00:00
|
|
|
err = sscanf(name1, "shm:%256[^,],%256[^,]", socket, sname);
|
2000-11-20 20:10:46 +00:00
|
|
|
if (err == 2)
|
2001-02-11 15:45:35 +00:00
|
|
|
return snd_pcm_shm_open(pcmp, name, socket, sname, stream, mode);
|
2001-03-17 16:34:43 +00:00
|
|
|
err = sscanf(name1, "file:%256[^,],%16[^,],%256[^,]", file, format, sname);
|
2001-03-01 22:46:47 +00:00
|
|
|
if (err == 3) {
|
|
|
|
|
snd_pcm_t *slave;
|
|
|
|
|
err = snd_pcm_open(&slave, sname, stream, mode);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-03-17 16:34:43 +00:00
|
|
|
return snd_pcm_file_open(pcmp, name1, file, -1, format, slave, 1);
|
2001-03-01 22:46:47 +00:00
|
|
|
}
|
2001-03-17 16:34:43 +00:00
|
|
|
err = sscanf(name1, "file:%256[^,],%16[^,]", file, format);
|
2000-11-20 20:10:46 +00:00
|
|
|
if (err == 2) {
|
|
|
|
|
snd_pcm_t *slave;
|
2001-02-11 15:45:35 +00:00
|
|
|
err = snd_pcm_null_open(&slave, name, stream, mode);
|
2000-11-20 20:10:46 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-02-11 15:45:35 +00:00
|
|
|
return snd_pcm_file_open(pcmp, name, file, -1, format, slave, 1);
|
2000-11-20 20:10:46 +00:00
|
|
|
}
|
2001-03-17 16:34:43 +00:00
|
|
|
err = sscanf(name1, "file:%256[^,]", file);
|
2000-11-20 20:10:46 +00:00
|
|
|
if (err == 1) {
|
|
|
|
|
snd_pcm_t *slave;
|
2001-02-11 15:45:35 +00:00
|
|
|
err = snd_pcm_null_open(&slave, name, stream, mode);
|
2000-11-20 20:10:46 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
return err;
|
2001-02-11 15:45:35 +00:00
|
|
|
return snd_pcm_file_open(pcmp, name, file, -1, "raw", slave, 1);
|
2000-11-20 20:10:46 +00:00
|
|
|
}
|
2001-03-17 16:34:43 +00:00
|
|
|
if (strcmp(name1, "null") == 0)
|
2001-02-11 15:45:35 +00:00
|
|
|
return snd_pcm_null_open(pcmp, name, stream, mode);
|
2001-03-17 16:34:43 +00:00
|
|
|
SNDERR("Unknown PCM %s", name1);
|
2000-11-20 20:10:46 +00:00
|
|
|
return -ENOENT;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2001-02-07 11:34:33 +00:00
|
|
|
if (snd_config_get_type(pcm_conf) != SND_CONFIG_TYPE_COMPOUND) {
|
2001-03-17 16:34:43 +00:00
|
|
|
SNDERR("Invalid type for PCM %s definition", name1);
|
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) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("type is not defined");
|
2000-09-24 09:57:26 +00:00
|
|
|
return err;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2001-02-07 11:34:33 +00:00
|
|
|
err = snd_config_get_string(conf, &str);
|
2000-10-14 10:31:34 +00:00
|
|
|
if (err < 0) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("Invalid type for %s", snd_config_get_id(conf));
|
2000-09-24 09:57:26 +00:00
|
|
|
return err;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2001-03-17 16:34:43 +00:00
|
|
|
err = snd_config_search_alias(snd_config, "pcm_type", str, &type_conf);
|
2001-03-07 12:36:05 +00:00
|
|
|
if (err >= 0) {
|
2001-03-17 16:34:43 +00:00
|
|
|
if (snd_config_get_type(type_conf) != SND_CONFIG_TYPE_COMPOUND) {
|
|
|
|
|
SNDERR("Invalid type for PCM type %s definition", str);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
2001-03-07 12:36:05 +00:00
|
|
|
snd_config_for_each(i, next, type_conf) {
|
|
|
|
|
snd_config_t *n = snd_config_iterator_entry(i);
|
|
|
|
|
const char *id = snd_config_get_id(n);
|
|
|
|
|
if (strcmp(id, "comment") == 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (strcmp(id, "lib") == 0) {
|
|
|
|
|
err = snd_config_get_string(n, &lib);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
SNDERR("Invalid type for %s", id);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2001-03-07 12:36:05 +00:00
|
|
|
if (strcmp(id, "open") == 0) {
|
|
|
|
|
err = snd_config_get_string(n, &open);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
SNDERR("Invalid type for %s", id);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
continue;
|
2000-10-14 10:31:34 +00:00
|
|
|
}
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("Unknown field %s", 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) {
|
2001-03-07 12:36:05 +00:00
|
|
|
open = buf;
|
|
|
|
|
snprintf(buf, sizeof(buf), "_snd_pcm_%s_open", str);
|
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) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("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);
|
2000-10-14 10:31:34 +00:00
|
|
|
if (!open_func) {
|
2001-03-04 20:39:02 +00:00
|
|
|
SNDERR("symbol %s is not defined inside %s", open, lib);
|
2001-03-07 12:36:05 +00:00
|
|
|
dlclose(h);
|
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
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Wait for a PCM to become ready
|
|
|
|
|
* \param pcm a PCM handle
|
|
|
|
|
* \param timeout maximum time in milliseconds to wait
|
|
|
|
|
* \return a negative error code on failure or zero on success
|
|
|
|
|
*/
|
2000-09-24 09:57:26 +00:00
|
|
|
int snd_pcm_wait(snd_pcm_t *pcm, int timeout)
|
|
|
|
|
{
|
|
|
|
|
struct pollfd pfd;
|
|
|
|
|
int err;
|
2001-02-12 23:51:49 +00:00
|
|
|
err = snd_pcm_poll_descriptors(pcm, &pfd, 1);
|
|
|
|
|
assert(err == 1);
|
2000-09-24 09:57:26 +00:00
|
|
|
err = poll(&pfd, 1, timeout);
|
2000-08-26 08:10:53 +00:00
|
|
|
if (err < 0)
|
2001-02-04 17:03:17 +00:00
|
|
|
return -errno;
|
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
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Return number of frames ready to be read/written
|
|
|
|
|
* \param pcm a PCM handle
|
|
|
|
|
* \return a positive number of frames ready otherwise a negative
|
|
|
|
|
* error code
|
|
|
|
|
*
|
|
|
|
|
* On capture does all the actions needed to transport to application
|
|
|
|
|
* level all the ready frames across underlying layers.
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_sframes_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);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Advance PCM frame position in mmap buffer
|
|
|
|
|
* \param pcm a PCM handle
|
|
|
|
|
* \param size movement size
|
|
|
|
|
* \return a positive number of actual movement size otherwise a negative
|
|
|
|
|
* error code
|
|
|
|
|
*
|
|
|
|
|
* On playback does all the actions needed to transport the frames across
|
|
|
|
|
* underlying layers.
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_sframes_t snd_pcm_mmap_forward(snd_pcm_t *pcm, snd_pcm_uframes_t size)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Silence an area
|
|
|
|
|
* \param dst_area area specification
|
|
|
|
|
* \param dst_offset offset in frames inside area
|
|
|
|
|
* \param samples samples to silence
|
|
|
|
|
* \param format PCM sample format
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
int snd_pcm_area_silence(const snd_pcm_channel_area_t *dst_area, snd_pcm_uframes_t dst_offset,
|
2001-02-04 17:03:17 +00:00
|
|
|
unsigned int samples, snd_pcm_format_t format)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
|
|
|
|
/* 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) {
|
2001-01-15 11:06:53 +00:00
|
|
|
unsigned int dwords = samples * width / 64;
|
2000-09-24 09:57:26 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Silence one or more areas
|
|
|
|
|
* \param dst_areas areas specification (one for each channel)
|
|
|
|
|
* \param dst_offset offset in frames inside area
|
|
|
|
|
* \param channels channels count
|
|
|
|
|
* \param frames frames to silence
|
|
|
|
|
* \param format PCM sample format
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-15 11:06:53 +00:00
|
|
|
int snd_pcm_areas_silence(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
|
2001-02-04 17:03:17 +00:00
|
|
|
unsigned int channels, snd_pcm_uframes_t frames, snd_pcm_format_t format)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Copy an area
|
|
|
|
|
* \param dst_area destination area specification
|
|
|
|
|
* \param dst_offset offset in frames inside destination area
|
|
|
|
|
* \param src_area source area specification
|
|
|
|
|
* \param src_offset offset in frames inside source area
|
|
|
|
|
* \param samples samples to copy
|
|
|
|
|
* \param format PCM sample format
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-31 17:26:56 +00:00
|
|
|
int snd_pcm_area_copy(const snd_pcm_channel_area_t *dst_area, snd_pcm_uframes_t dst_offset,
|
|
|
|
|
const snd_pcm_channel_area_t *src_area, snd_pcm_uframes_t src_offset,
|
2001-02-04 17:03:17 +00:00
|
|
|
unsigned int samples, snd_pcm_format_t format)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
|
|
|
|
/* FIXME: sub byte resolution and odd dst_offset */
|
2001-02-11 15:45:35 +00:00
|
|
|
const char *src;
|
|
|
|
|
char *dst;
|
2000-09-24 09:57:26 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
/**
|
|
|
|
|
* \brief Copy one or more areas
|
|
|
|
|
* \param dst_areas destination areas specification (one for each channel)
|
|
|
|
|
* \param dst_offset offset in frames inside destination area
|
|
|
|
|
* \param src_areas source areas specification (one for each channel)
|
|
|
|
|
* \param src_offset offset in frames inside source area
|
|
|
|
|
* \param channels channels count
|
|
|
|
|
* \param frames frames to copy
|
|
|
|
|
* \param format PCM sample format
|
|
|
|
|
* \return zero on success otherwise a negative error code
|
|
|
|
|
*/
|
2001-01-31 17:26:56 +00:00
|
|
|
int snd_pcm_areas_copy(const snd_pcm_channel_area_t *dst_areas, snd_pcm_uframes_t dst_offset,
|
|
|
|
|
const snd_pcm_channel_area_t *src_areas, snd_pcm_uframes_t src_offset,
|
2001-02-04 17:03:17 +00:00
|
|
|
unsigned int channels, snd_pcm_uframes_t frames, snd_pcm_format_t 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;
|
2001-01-31 17:26:56 +00:00
|
|
|
snd_pcm_area_copy(&d, dst_offset * chns,
|
|
|
|
|
&s, src_offset * chns,
|
|
|
|
|
frames * chns, format);
|
2000-09-24 09:57:26 +00:00
|
|
|
channels -= chns;
|
|
|
|
|
} else {
|
2001-01-31 17:26:56 +00:00
|
|
|
snd_pcm_area_copy(dst_start, dst_offset,
|
|
|
|
|
src_start, src_offset,
|
|
|
|
|
frames, format);
|
2000-09-24 09:57:26 +00:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
#ifndef DOC_HIDDEN
|
|
|
|
|
|
|
|
|
|
int _snd_pcm_poll_descriptor(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
assert(pcm);
|
|
|
|
|
return pcm->poll_fd;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void snd_pcm_areas_from_buf(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
|
|
|
|
|
void *buf)
|
|
|
|
|
{
|
|
|
|
|
unsigned int channel;
|
|
|
|
|
unsigned int channels = pcm->channels;
|
|
|
|
|
for (channel = 0; channel < channels; ++channel, ++areas) {
|
|
|
|
|
areas->addr = buf;
|
|
|
|
|
areas->first = channel * pcm->sample_bits;
|
|
|
|
|
areas->step = pcm->frame_bits;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void snd_pcm_areas_from_bufs(snd_pcm_t *pcm, snd_pcm_channel_area_t *areas,
|
|
|
|
|
void **bufs)
|
|
|
|
|
{
|
|
|
|
|
unsigned int channel;
|
|
|
|
|
unsigned int channels = pcm->channels;
|
|
|
|
|
for (channel = 0; channel < channels; ++channel, ++areas, ++bufs) {
|
|
|
|
|
areas->addr = *bufs;
|
|
|
|
|
areas->first = 0;
|
|
|
|
|
areas->step = pcm->sample_bits;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_sframes_t snd_pcm_read_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
|
2001-01-20 12:56:30 +00:00
|
|
|
snd_pcm_uframes_t offset, snd_pcm_uframes_t size,
|
|
|
|
|
snd_pcm_xfer_areas_func_t func)
|
2000-09-24 09:57:26 +00:00
|
|
|
{
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_uframes_t xfer = 0;
|
2001-01-20 12:56:30 +00:00
|
|
|
int err = 0;
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_state_t state = snd_pcm_state(pcm);
|
2001-01-20 12:56:30 +00:00
|
|
|
|
|
|
|
|
if (size == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
if (size > pcm->xfer_align)
|
|
|
|
|
size -= size % pcm->xfer_align;
|
|
|
|
|
|
2001-02-04 17:03:17 +00:00
|
|
|
switch (snd_enum_to_int(state)) {
|
2001-01-20 12:56:30 +00:00
|
|
|
case SND_PCM_STATE_PREPARED:
|
|
|
|
|
if (pcm->start_mode == SND_PCM_START_DATA) {
|
|
|
|
|
err = snd_pcm_start(pcm);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto _end;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_STATE_DRAINING:
|
|
|
|
|
case SND_PCM_STATE_RUNNING:
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_STATE_XRUN:
|
|
|
|
|
return -EPIPE;
|
|
|
|
|
default:
|
|
|
|
|
return -EBADFD;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2001-01-20 12:56:30 +00:00
|
|
|
|
|
|
|
|
while (size > 0) {
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_uframes_t frames;
|
2001-01-20 12:56:30 +00:00
|
|
|
snd_pcm_sframes_t avail;
|
|
|
|
|
_again:
|
2000-09-24 09:57:26 +00:00
|
|
|
avail = snd_pcm_avail_update(pcm);
|
|
|
|
|
if (avail < 0) {
|
2001-01-20 12:56:30 +00:00
|
|
|
err = -EPIPE;
|
|
|
|
|
goto _end;
|
2000-08-31 11:21:05 +00:00
|
|
|
}
|
2001-01-20 12:56:30 +00:00
|
|
|
if (state == SND_PCM_STATE_DRAINING) {
|
|
|
|
|
if (avail == 0) {
|
2000-09-24 09:57:26 +00:00
|
|
|
err = -EPIPE;
|
2001-01-20 12:56:30 +00:00
|
|
|
goto _end;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2001-01-20 12:56:30 +00:00
|
|
|
} else if (avail == 0 ||
|
|
|
|
|
(size >= pcm->xfer_align &&
|
|
|
|
|
(snd_pcm_uframes_t) avail < pcm->xfer_align)) {
|
2000-09-24 09:57:26 +00:00
|
|
|
if (pcm->mode & SND_PCM_NONBLOCK) {
|
|
|
|
|
err = -EAGAIN;
|
2001-01-20 12:56:30 +00:00
|
|
|
goto _end;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2001-01-20 12:56:30 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
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);
|
2001-01-20 12:56:30 +00:00
|
|
|
goto _again;
|
|
|
|
|
|
2000-08-31 11:21:05 +00:00
|
|
|
}
|
2001-01-20 12:56:30 +00:00
|
|
|
if ((snd_pcm_uframes_t) avail > pcm->xfer_align)
|
|
|
|
|
avail -= avail % pcm->xfer_align;
|
|
|
|
|
frames = size;
|
|
|
|
|
if (frames > (snd_pcm_uframes_t) avail)
|
2000-09-24 09:57:26 +00:00
|
|
|
frames = avail;
|
2001-01-20 12:56:30 +00:00
|
|
|
assert(frames != 0);
|
2001-02-27 13:42:12 +00:00
|
|
|
err = func(pcm, areas, offset, frames);
|
2000-09-24 09:57:26 +00:00
|
|
|
if (err < 0)
|
|
|
|
|
break;
|
2001-01-15 11:06:53 +00:00
|
|
|
assert((snd_pcm_uframes_t)err == frames);
|
2001-01-20 12:56:30 +00:00
|
|
|
offset += frames;
|
|
|
|
|
size -= frames;
|
|
|
|
|
xfer += frames;
|
|
|
|
|
#if 0
|
|
|
|
|
state = snd_pcm_state(pcm);
|
|
|
|
|
if (state == SND_PCM_STATE_XRUN) {
|
|
|
|
|
err = -EPIPE;
|
|
|
|
|
goto _end;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2000-08-31 11:21:05 +00:00
|
|
|
}
|
2001-01-20 12:56:30 +00:00
|
|
|
_end:
|
|
|
|
|
return xfer > 0 ? xfer : err;
|
2000-08-31 11:21:05 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_sframes_t snd_pcm_write_areas(snd_pcm_t *pcm, const snd_pcm_channel_area_t *areas,
|
2001-01-20 12:56:30 +00:00
|
|
|
snd_pcm_uframes_t offset, snd_pcm_uframes_t size,
|
|
|
|
|
snd_pcm_xfer_areas_func_t func)
|
2000-08-24 17:07:44 +00:00
|
|
|
{
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_uframes_t xfer = 0;
|
2001-01-20 12:56:30 +00:00
|
|
|
int err = 0;
|
2001-02-04 17:03:17 +00:00
|
|
|
snd_pcm_state_t state = snd_pcm_state(pcm);
|
2001-01-20 12:56:30 +00:00
|
|
|
|
|
|
|
|
if (size == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
if (size > pcm->xfer_align)
|
|
|
|
|
size -= size % pcm->xfer_align;
|
|
|
|
|
|
2001-02-04 17:03:17 +00:00
|
|
|
switch (snd_enum_to_int(state)) {
|
2001-01-20 12:56:30 +00:00
|
|
|
case SND_PCM_STATE_PREPARED:
|
|
|
|
|
case SND_PCM_STATE_RUNNING:
|
|
|
|
|
break;
|
|
|
|
|
case SND_PCM_STATE_XRUN:
|
|
|
|
|
return -EPIPE;
|
|
|
|
|
default:
|
|
|
|
|
return -EBADFD;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (size > 0) {
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_uframes_t frames;
|
2001-01-20 12:56:30 +00:00
|
|
|
snd_pcm_sframes_t avail;
|
|
|
|
|
_again:
|
2000-09-24 09:57:26 +00:00
|
|
|
avail = snd_pcm_avail_update(pcm);
|
|
|
|
|
if (avail < 0) {
|
2001-01-20 12:56:30 +00:00
|
|
|
err = -EPIPE;
|
|
|
|
|
goto _end;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2001-01-20 12:56:30 +00:00
|
|
|
if (state == SND_PCM_STATE_PREPARED) {
|
|
|
|
|
if (avail == 0) {
|
2000-09-24 09:57:26 +00:00
|
|
|
err = -EPIPE;
|
2001-01-20 12:56:30 +00:00
|
|
|
goto _end;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2001-01-20 12:56:30 +00:00
|
|
|
} else if (avail == 0 ||
|
|
|
|
|
(size >= pcm->xfer_align &&
|
|
|
|
|
(snd_pcm_uframes_t) avail < pcm->xfer_align)) {
|
2000-09-24 09:57:26 +00:00
|
|
|
if (pcm->mode & SND_PCM_NONBLOCK) {
|
|
|
|
|
err = -EAGAIN;
|
2001-01-20 12:56:30 +00:00
|
|
|
goto _end;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2001-01-20 12:56:30 +00:00
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
err = snd_pcm_wait(pcm, -1);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
break;
|
|
|
|
|
state = snd_pcm_state(pcm);
|
2001-01-20 12:56:30 +00:00
|
|
|
goto _again;
|
|
|
|
|
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2001-01-20 12:56:30 +00:00
|
|
|
if ((snd_pcm_uframes_t) avail > pcm->xfer_align)
|
|
|
|
|
avail -= avail % pcm->xfer_align;
|
|
|
|
|
frames = size;
|
|
|
|
|
if (frames > (snd_pcm_uframes_t) avail)
|
2000-09-24 09:57:26 +00:00
|
|
|
frames = avail;
|
2001-01-20 12:56:30 +00:00
|
|
|
assert(frames != 0);
|
2001-02-27 13:42:12 +00:00
|
|
|
err = func(pcm, areas, offset, frames);
|
2000-08-24 17:07:44 +00:00
|
|
|
if (err < 0)
|
2000-09-24 09:57:26 +00:00
|
|
|
break;
|
2001-01-15 11:06:53 +00:00
|
|
|
assert((snd_pcm_uframes_t)err == frames);
|
2001-01-20 12:56:30 +00:00
|
|
|
offset += frames;
|
|
|
|
|
size -= frames;
|
|
|
|
|
xfer += frames;
|
|
|
|
|
#if 0
|
|
|
|
|
state = snd_pcm_state(pcm);
|
|
|
|
|
if (state == SND_PCM_STATE_XRUN) {
|
|
|
|
|
err = -EPIPE;
|
|
|
|
|
goto _end;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2000-09-24 09:57:26 +00:00
|
|
|
if (state == SND_PCM_STATE_PREPARED &&
|
2001-01-20 12:56:30 +00:00
|
|
|
pcm->start_mode == SND_PCM_START_DATA) {
|
2000-09-24 09:57:26 +00:00
|
|
|
err = snd_pcm_start(pcm);
|
|
|
|
|
if (err < 0)
|
2001-01-20 12:56:30 +00:00
|
|
|
goto _end;
|
2000-09-24 09:57:26 +00:00
|
|
|
}
|
2000-08-24 17:07:44 +00:00
|
|
|
}
|
2001-01-20 12:56:30 +00:00
|
|
|
_end:
|
|
|
|
|
return xfer > 0 ? xfer : err;
|
2000-08-24 17:07:44 +00:00
|
|
|
}
|
2000-09-24 09:57:26 +00:00
|
|
|
|
2001-01-15 11:06:53 +00:00
|
|
|
snd_pcm_uframes_t _snd_pcm_mmap_hw_ptr(snd_pcm_t *pcm)
|
2000-11-22 14:27:37 +00:00
|
|
|
{
|
|
|
|
|
return *pcm->hw_ptr;
|
|
|
|
|
}
|
2000-11-26 12:16:18 +00:00
|
|
|
|
2001-01-31 17:26:56 +00:00
|
|
|
snd_pcm_uframes_t _snd_pcm_boundary(snd_pcm_t *pcm)
|
|
|
|
|
{
|
|
|
|
|
return pcm->boundary;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-17 16:34:43 +00:00
|
|
|
static const char *names[SND_PCM_HW_PARAM_LAST + 1] = {
|
|
|
|
|
[SND_PCM_HW_PARAM_FORMAT] = "format",
|
|
|
|
|
[SND_PCM_HW_PARAM_CHANNELS] = "channels",
|
|
|
|
|
[SND_PCM_HW_PARAM_RATE] = "rate",
|
|
|
|
|
[SND_PCM_HW_PARAM_PERIOD_TIME] = "period_time",
|
|
|
|
|
[SND_PCM_HW_PARAM_BUFFER_TIME] = "buffer_time"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int snd_pcm_slave_conf(snd_config_t *conf, const char **namep,
|
|
|
|
|
unsigned int count, ...)
|
|
|
|
|
{
|
|
|
|
|
snd_config_iterator_t i, next;
|
|
|
|
|
const char *str;
|
|
|
|
|
struct {
|
|
|
|
|
unsigned int index;
|
|
|
|
|
int mandatory;
|
|
|
|
|
void *ptr;
|
|
|
|
|
int valid;
|
|
|
|
|
} fields[count];
|
|
|
|
|
unsigned int k;
|
|
|
|
|
int pcm_valid = 0;
|
|
|
|
|
int err;
|
|
|
|
|
va_list args;
|
|
|
|
|
if (snd_config_get_string(conf, &str) >= 0) {
|
|
|
|
|
err = snd_config_search_alias(snd_config, "pcm_slave", str, &conf);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
SNDERR("unkown pcm_slave %s", str);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
va_start(args, count);
|
|
|
|
|
for (k = 0; k < count; ++k) {
|
|
|
|
|
fields[k].index = va_arg(args, int);
|
|
|
|
|
fields[k].mandatory = va_arg(args, int);
|
|
|
|
|
fields[k].ptr = va_arg(args, void *);
|
|
|
|
|
fields[k].valid = 0;
|
|
|
|
|
}
|
|
|
|
|
va_end(args);
|
|
|
|
|
snd_config_for_each(i, next, conf) {
|
|
|
|
|
snd_config_t *n = snd_config_iterator_entry(i);
|
|
|
|
|
const char *id = snd_config_get_id(n);
|
|
|
|
|
if (strcmp(id, "comment") == 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (strcmp(id, "pcm") == 0) {
|
|
|
|
|
if (pcm_valid) {
|
|
|
|
|
_duplicated:
|
|
|
|
|
SNDERR("duplicated %s", id);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
err = snd_config_get_string(n, namep);
|
|
|
|
|
if (err < 0) {
|
|
|
|
|
_invalid:
|
|
|
|
|
SNDERR("invalid type for %s", id);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
pcm_valid = 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (k = 0; k < count; ++k) {
|
|
|
|
|
unsigned int idx = fields[k].index;
|
|
|
|
|
long v;
|
|
|
|
|
assert(idx < SND_PCM_HW_PARAM_LAST);
|
|
|
|
|
assert(names[idx]);
|
|
|
|
|
if (strcmp(id, names[idx]) != 0)
|
|
|
|
|
continue;
|
|
|
|
|
if (fields[k].valid)
|
|
|
|
|
goto _duplicated;
|
|
|
|
|
switch (idx) {
|
|
|
|
|
case SND_PCM_HW_PARAM_FORMAT:
|
|
|
|
|
{
|
|
|
|
|
snd_pcm_format_t f;
|
|
|
|
|
err = snd_config_get_string(n, &str);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto _invalid;
|
|
|
|
|
f = snd_pcm_format_value(str);
|
|
|
|
|
if (f == SND_PCM_FORMAT_UNKNOWN) {
|
|
|
|
|
SNDERR("unknown format");
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
*(snd_pcm_format_t*)fields[k].ptr = f;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
err = snd_config_get_integer(n, &v);
|
|
|
|
|
if (err < 0)
|
|
|
|
|
goto _invalid;
|
|
|
|
|
*(int*)fields[k].ptr = v;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
fields[k].valid = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (k < count)
|
|
|
|
|
continue;
|
|
|
|
|
SNDERR("Unknown field %s", id);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
for (k = 0; k < count; ++k) {
|
|
|
|
|
if (fields[k].mandatory && !fields[k].valid) {
|
|
|
|
|
SNDERR("missing field %s", names[fields[k].index]);
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2001-03-23 11:05:41 +00:00
|
|
|
#endif
|