removed ordinary stuff

This commit is contained in:
Jaroslav Kysela 2005-05-30 11:57:26 +00:00
parent ff9fd33190
commit 41398ceb9c
7 changed files with 1 additions and 1882 deletions

View file

@ -1,219 +0,0 @@
/**
* \file include/mixer_ordinary.h
* \brief Application interface library for the ALSA driver
* \author Jaroslav Kysela <perex@suse.cz>
* \date 2003
*
* Application interface library for the ALSA driver.
* See the \ref mixer_ordinary page for more details.
*/
/*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef __ALSA_MIXER_SIMPLE_H
#define __ALSA_MIXER_SIMPLE_H
#include <alsa/asoundlib.h>
/*
* Abbreviations:
*
* FLVOL - Front Left Volume (0-1000)
* FCLVOL - Front Center Left Volume (0-1000)
* FCVOL - Front Center Volume (0-1000)
* FCRVOL - Front Center Right Volume (0-1000)
* FRVOL - Front Right Volume (0-1000)
* FSLVOL - Front Side Left Volume (0-1000)
* FSRVOL - Front Side Right Volume (0-1000)
* RSLVOL - Rear Side Left Volume (0-1000)
* RSRVOL - Rear Side Right Volume (0-1000)
* RLVOL - Rear Left Volume (0-1000)
* RCVOL - Rear Center Volume (0-1000)
* RRVOL - Rear Right Volume (0-1000)
* LFEVOL - Low Frequency Effects (Subwoofer) Volume (0-1000)
* OVRVOL - Overhead Volume (0-1000)
*/
/** Ordinary Mixer I/O type */
enum sndo_mixer_io_type {
/*
* playback section
*/
/* Master */
SNDO_MIO_MASTER_FLVOL = 0 * 0x40,
SNDO_MIO_MASTER_FCLVOL,
SNDO_MIO_MASTER_FCVOL,
SNDO_MIO_MASTER_FCRVOL,
SNDO_MIO_MASTER_FRVOL,
SNDO_MIO_MASTER_FSLVOL,
SNDO_MIO_MASTER_FSRVOL,
SNDO_MIO_MASTER_RSLVOL,
SNDO_MIO_MASTER_RSRVOL,
SNDO_MIO_MASTER_RLVOL,
SNDO_MIO_MASTER_RCVOL,
SNDO_MIO_MASTER_RRVOL,
SNDO_MIO_MASTER_LFEVOL,
SNDO_MIO_MASTER_OVRVOL,
/* PCM */
SNDO_MIO_PCM_FLVOL = 1 * 0x40,
SNDO_MIO_PCM_FCLVOL,
SNDO_MIO_PCM_FCVOL,
SNDO_MIO_PCM_FCRVOL,
SNDO_MIO_PCM_FRVOL,
SNDO_MIO_PCM_FSLVOL,
SNDO_MIO_PCM_FSRVOL,
SNDO_MIO_PCM_RSLVOL,
SNDO_MIO_PCM_RSRVOL,
SNDO_MIO_PCM_RLVOL,
SNDO_MIO_PCM_RCVOL,
SNDO_MIO_PCM_RRVOL,
SNDO_MIO_PCM_LFEVOL,
SNDO_MIO_PCM_OVRVOL,
/* LINE */
SNDO_MIO_LINE_FLVOL = 2 * 0x40,
SNDO_MIO_LINE_FCLVOL,
SNDO_MIO_LINE_FCVOL,
SNDO_MIO_LINE_FCRVOL,
SNDO_MIO_LINE_FRVOL,
SNDO_MIO_LINE_FSLVOL,
SNDO_MIO_LINE_FSRVOL,
SNDO_MIO_LINE_RSLVOL,
SNDO_MIO_LINE_RSRVOL,
SNDO_MIO_LINE_RLVOL,
SNDO_MIO_LINE_RCVOL,
SNDO_MIO_LINE_RRVOL,
SNDO_MIO_LINE_LFEVOL,
SNDO_MIO_LINE_OVRVOL,
/* MIC */
SNDO_MIO_MIC_FLVOL = 3 * 0x40,
SNDO_MIO_MIC_FCLVOL,
SNDO_MIO_MIC_FCVOL,
SNDO_MIO_MIC_FCRVOL,
SNDO_MIO_MIC_FRVOL,
SNDO_MIO_MIC_FSLVOL,
SNDO_MIO_MIC_FSRVOL,
SNDO_MIO_MIC_RSLVOL,
SNDO_MIO_MIC_RSRVOL,
SNDO_MIO_MIC_RLVOL,
SNDO_MIO_MIC_RCVOL,
SNDO_MIO_MIC_RRVOL,
SNDO_MIO_MIC_LFEVOL,
SNDO_MIO_MIC_OVRVOL,
/* CD */
SNDO_MIO_CD_FLVOL = 4 * 0x40,
SNDO_MIO_CD_FCLVOL,
SNDO_MIO_CD_FCVOL,
SNDO_MIO_CD_FCRVOL,
SNDO_MIO_CD_FRVOL,
SNDO_MIO_CD_FSLVOL,
SNDO_MIO_CD_FSRVOL,
SNDO_MIO_CD_RSLVOL,
SNDO_MIO_CD_RSRVOL,
SNDO_MIO_CD_RLVOL,
SNDO_MIO_CD_RCVOL,
SNDO_MIO_CD_RRVOL,
SNDO_MIO_CD_LFEVOL,
SNDO_MIO_CD_OVRVOL,
/* AUX */
SNDO_MIO_AUX_FLVOL = 5 * 0x40,
SNDO_MIO_AUX_FCLVOL,
SNDO_MIO_AUX_FCVOL,
SNDO_MIO_AUX_FCRVOL,
SNDO_MIO_AUX_FRVOL,
SNDO_MIO_AUX_FSLVOL,
SNDO_MIO_AUX_FSRVOL,
SNDO_MIO_AUX_RSLVOL,
SNDO_MIO_AUX_RSRVOL,
SNDO_MIO_AUX_RLVOL,
SNDO_MIO_AUX_RCVOL,
SNDO_MIO_AUX_RRVOL,
SNDO_MIO_AUX_LFEVOL,
SNDO_MIO_AUX_OVRVOL,
/*
* capture section
*/
/* capture gain */
SNDO_MIO_CGAIN_FL = 0x8000,
SNDO_MIO_CGAIN_FCL,
SNDO_MIO_CGAIN_FC,
SNDO_MIO_CGAIN_FCR,
SNDO_MIO_CGAIN_FR,
SNDO_MIO_CGAIN_FSL,
SNDO_MIO_CGAIN_FSR,
SNDO_MIO_CGAIN_RSL,
SNDO_MIO_CGAIN_RSR,
SNDO_MIO_CGAIN_RL,
SNDO_MIO_CGAIN_RC,
SNDO_MIO_CGAIN_RR,
SNDO_MIO_CGAIN_LFE,
SNDO_MIO_CGAIN_OVR,
/* capture source (0 = off, 1 = on) */
SNDO_MIO_CSOURCE_MIC = 0x8100,
SNDO_MIO_CSOURCE_LINE,
SNDO_MIO_CSOURCE_CD,
SNDO_MIO_CSOURCE_AUX,
SNDO_MIO_CSOURCE_MIX,
/* misc */
SNDO_MIO_STEREO = 0x8200, /* (0 = off, 1 = on) standard stereo source, might be converted to use all outputs */
};
typedef struct sndo_mixer sndo_mixer_t;
struct alisp_cfg;
#ifdef __cplusplus
extern "C" {
#endif
/**
* \defgroup Mixer_ordinary Mixer Ordinary Interface
* See the \ref mixer_ordinary page for more details.
* \{
*/
int sndo_mixer_open(sndo_mixer_t **pmixer, const char *playback_name, const char *capture_name, struct alisp_cfg *lconf);
int sndo_mixer_open_pcm(sndo_mixer_t **pmixer, snd_pcm_t *playback_pcm, snd_pcm_t *capture_pcm, struct alisp_cfg *lconf);
int sndo_mixer_close(sndo_mixer_t *mixer);
int sndo_mixer_poll_descriptors_count(sndo_mixer_t *mixer);
int sndo_mixer_poll_descriptors(sndo_mixer_t *mixer, struct pollfd *pfds, unsigned int space);
int sndo_mixer_poll_descriptors_revents(sndo_mixer_t *mixer, struct pollfd *pfds, unsigned int nfds, unsigned short *revents);
int sndo_mixer_io_get_name(enum sndo_mixer_io_type type, char **name);
int sndo_mixer_io_get(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val);
int sndo_mixer_io_set(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val);
int sndo_mixer_io_try_set(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val);
int sndo_mixer_io_get_dB(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val);
int sndo_mixer_io_set_dB(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val);
int sndo_mixer_io_try_set_dB(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val);
int sndo_mixer_io_change(sndo_mixer_t *mixer, enum sndo_mixer_io_type *changed, int changed_array_size);
/** \} */
#ifdef __cplusplus
}
#endif
#endif /* __ALSA_MIXER_SIMPLE_H */

View file

@ -1,145 +0,0 @@
/**
* \file include/pcm_ordinary.h
* \brief Application interface library for the ALSA driver
* \author Jaroslav Kysela <perex@suse.cz>
* \date 2003
*
* Application interface library for the ALSA driver.
* See the \ref pcm_ordinary page for more details.
*/
/*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef __ALSA_PCM_ORDINARY_H
#define __ALSA_PCM_ORDINARY_H
#include <alsa/asoundlib.h>
/** Ordinary PCM latency type */
enum sndo_pcm_latency_type {
/** normal latency - for standard playback or capture
(estimated latency in one direction 350ms) (default) */
SNDO_PCM_LATENCY_NORMAL = 0,
/** medium latency - software phones etc.
(estimated latency in one direction maximally 50ms) */
SNDO_PCM_LATENCY_MEDIUM,
/** realtime latency - realtime applications (effect processors etc.)
(estimated latency in one direction 5ms) */
SNDO_PCM_LATENCY_REALTIME
};
/** Ordinary PCM access type */
enum sndo_pcm_access_type {
/** interleaved access - channels are interleaved without any gaps among samples (default) */
SNDO_PCM_ACCESS_INTERLEAVED = 0,
/** noninterleaved access - channels are separate without any gaps among samples */
SNDO_PCM_ACCESS_NONINTERLEAVED
};
/** Ordinary PCM xrun type */
enum sndo_pcm_xrun_type {
/** driver / library will ignore all xruns, the stream runs forever (default) */
SNDO_PCM_XRUN_IGNORE = 0,
/** driver / library stops the stream when an xrun occurs */
SNDO_PCM_XRUN_STOP
};
typedef struct sndo_pcm sndo_pcm_t;
typedef int (sndo_pcm_engine_callback_t)(sndo_pcm_t *pcm);
#ifdef __cplusplus
extern "C" {
#endif
/**
* \defgroup PCM_ordinary PCM Ordinary Interface
* See the \ref pcm_ordinary page for more details.
* \{
*/
int sndo_pcm_open(sndo_pcm_t **pcm, const char *playback_name, const char *capture_name, struct alisp_cfg *lconf);
int sndo_pcm_close(sndo_pcm_t *pcm);
int sndo_pcm_poll_descriptors_count(sndo_pcm_t *pcm);
int sndo_pcm_poll_descriptors(sndo_pcm_t *pcm, struct pollfd *pfds, unsigned int space);
int sndo_pcm_start(sndo_pcm_t *pcm);
int sndo_pcm_drop(sndo_pcm_t *pcm);
int sndo_pcm_drain(sndo_pcm_t *pcm);
int sndo_pcm_delay(sndo_pcm_t *pcm, snd_pcm_sframes_t *delayp);
int sndo_pcm_transfer_block(sndo_pcm_t *pcm, snd_pcm_uframes_t *tblock);
int sndo_pcm_resume(sndo_pcm_t *pcm);
int sndo_pcm_wait(sndo_pcm_t *pcm, int timeout);
snd_pcm_t *sndo_pcm_raw_playback(sndo_pcm_t *pcm);
snd_pcm_t *sndo_pcm_raw_capture(sndo_pcm_t *pcm);
/**
* \defgroup PCM_ordinary_params Parameters Functions
* \ingroup PCM_ordinary
* See the \ref pcm_ordinary page for more details.
* \{
*/
int sndo_pcm_param_reset(sndo_pcm_t *pcm);
int sndo_pcm_param_access(sndo_pcm_t *pcm, enum sndo_pcm_access_type access);
int sndo_pcm_param_rate(sndo_pcm_t *pcm, unsigned int rate, unsigned int *used_rate);
int sndo_pcm_param_channels(sndo_pcm_t *pcm, unsigned int channels);
int sndo_pcm_param_format(sndo_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_subformat_t subformat);
int sndo_pcm_param_latency(sndo_pcm_t *pcm, enum sndo_pcm_latency_type latency, snd_pcm_uframes_t *used_latency);
int sndo_pcm_param_xrun(sndo_pcm_t *pcm, enum sndo_pcm_xrun_type xrun);
/** \} */
/**
* \defgroup PCM_ordinary_access Ring Buffer I/O Functions
* \ingroup PCM_ordinary
* See the \ref pcm_ordinary page for more details.
* \{
*/
/* playback */
int sndo_pcm_pio_ibegin(sndo_pcm_t *pcm, void **ring_buffer, snd_pcm_uframes_t *frames);
snd_pcm_sframes_t sndo_pcm_pio_iend(sndo_pcm_t *pcm, snd_pcm_uframes_t frames);
int sndo_pcm_pio_nbegin(sndo_pcm_t *pcm, void ***ring_buffer, snd_pcm_uframes_t *frames);
snd_pcm_sframes_t sndo_pcm_pio_nend(sndo_pcm_t *pcm, snd_pcm_uframes_t frames);
/* capture */
int sndo_pcm_cio_ibegin(sndo_pcm_t *pcm, void **ring_buffer, snd_pcm_uframes_t *frames);
snd_pcm_sframes_t sndo_pcm_cio_iend(sndo_pcm_t *pcm, snd_pcm_uframes_t frames);
int sndo_pcm_cio_nbegin(sndo_pcm_t *pcm, void ***ring_buffer, snd_pcm_uframes_t *frames);
snd_pcm_sframes_t sndo_pcm_cio_nend(sndo_pcm_t *pcm, snd_pcm_uframes_t frames);
/** \} */
/**
* \defgroup PCM_ordinary_engine Callback like engine
* \ingroup PCM_ordinary
* See the \ref pcm_ordinary page for more details.
* \{
*/
int sndo_pcm_set_private_data(sndo_pcm_t *pcm, void *private_data);
int sndo_pcm_get_private_data(sndo_pcm_t *pcm, void **private_data);
int sndo_pcm_engine(sndo_pcm_t *pcm,
sndo_pcm_engine_callback_t *playback,
sndo_pcm_engine_callback_t *capture);
/** \} */
#ifdef __cplusplus
}
#endif
#endif /* __ALSA_PCM_ORDINARY_H */

View file

@ -1,4 +1,4 @@
SUBDIRS=control mixer ordinary_mixer pcm ordinary_pcm rawmidi timer hwdep seq instr compat conf alisp SUBDIRS=control mixer pcm rawmidi timer hwdep seq instr compat conf alisp
EXTRA_DIST=Versions EXTRA_DIST=Versions
COMPATNUM=@LIBTOOL_VERSION_INFO@ COMPATNUM=@LIBTOOL_VERSION_INFO@

View file

@ -1,13 +0,0 @@
#SUBDIRS =
#DIST_SUBDIRS =
EXTRA_LTLIBRARIES = libordinarymixer.la
libordinarymixer_la_SOURCES = ordinary_mixer.c
#noinst_HEADERS =
alsadir = $(datadir)/alsa
all: libordinarymixer.la
INCLUDES=-I$(top_srcdir)/include

View file

@ -1,565 +0,0 @@
/**
* \file ordinary_mixer/ordinary_mixer.c
* \ingroup Mixer_ordinary
* \brief Ordinary mixer interface
* \author Jaroslav Kysela <perex@suse.cz>
* \date 2003,2004
*
* Ordinary mixer interface is a high level abtraction for soundcard's
* mixing.
*
* See the \ref Mixer_ordinary page for more details.
*/
/*
* Ordinary Mixer Interface - main file
* Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/*! \page Mixer_ordinary Ordinary mixer interface
<P>Write something here</P>
\section Mixer_ordinary_overview
Write something here
*/
/**
* \example ../test/ordinary_mixer.c
* \anchor example_ordinary_mixer
*/
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdarg.h>
#include <signal.h>
#include <dlfcn.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <limits.h>
#include "local.h"
#include "alisp.h"
#include "mixer_ordinary.h"
struct sndo_mixer {
struct alisp_cfg *cfg;
struct alisp_instance *alisp;
int hctl_count;
snd_hctl_t **hctl;
int _free_cfg;
};
int sndo_mixer_open1(sndo_mixer_t **pmixer,
const char *lisp_fcn,
const char *lisp_fmt,
const void *parg,
const void *carg,
struct alisp_cfg *lconf)
{
struct alisp_cfg *cfg = lconf;
struct alisp_instance *alisp;
struct alisp_seq_iterator *iterator;
sndo_mixer_t *mixer;
int err, count;
long val;
*pmixer = NULL;
if (cfg == NULL) {
char *file;
snd_input_t *input;
file = getenv("ALSA_ORDINARY_MIXER");
if (!file)
file = DATADIR "/alsa/sndo-mixer.alisp";
if ((err = snd_input_stdio_open(&input, file, "r")) < 0) {
SNDERR("unable to open alisp file '%s'", file);
return err;
}
cfg = alsa_lisp_default_cfg(input);
if (cfg == NULL)
return -ENOMEM;
cfg->warning = 1;
#if 0
cfg->debug = 1;
cfg->verbose = 1;
#endif
}
err = alsa_lisp(cfg, &alisp);
if (err < 0)
goto __error;
err = alsa_lisp_function(alisp, &iterator, lisp_fcn, lisp_fmt, parg, carg);
if (err < 0) {
alsa_lisp_free(alisp);
goto __error;
}
err = alsa_lisp_seq_integer(iterator, &val);
if (err == 0 && val < 0)
err = val;
alsa_lisp_result_free(alisp, iterator);
if (err < 0) {
alsa_lisp_free(alisp);
goto __error;
}
count = 0;
if (alsa_lisp_seq_first(alisp, "hctls", &iterator) == 0) {
count = alsa_lisp_seq_count(iterator);
if (count < 0)
count = 0;
}
mixer = malloc(sizeof(sndo_mixer_t) + count * sizeof(snd_hctl_t *));
if (mixer == NULL) {
alsa_lisp_free(alisp);
err = -ENOMEM;
goto __error;
}
memset(mixer, 0, sizeof(sndo_mixer_t));
if (count > 0) {
mixer->hctl = (snd_hctl_t **)(mixer + 1);
do {
if (alsa_lisp_seq_pointer(iterator, "hctl", (void **)&mixer->hctl[mixer->hctl_count++]))
break;
} while (mixer->hctl_count < count && alsa_lisp_seq_next(&iterator) == 0);
if (mixer->hctl_count < count) {
mixer->hctl = NULL;
mixer->hctl_count = 0;
}
}
mixer->alisp = alisp;
mixer->cfg = cfg;
mixer->_free_cfg = cfg != lconf;
*pmixer = mixer;
return 0;
__error:
if (cfg != lconf)
alsa_lisp_default_cfg_free(cfg);
return err;
}
/**
* \brief Opens a ordinary mixer instance
* \param pmixer Returned ordinary mixer handle
* \param playback_name name for playback HCTL communication
* \param capture_name name for capture HCTL communication
* \param lconf Local configuration (might be NULL - use global configuration)
* \return 0 on success otherwise a negative error code
*/
int sndo_mixer_open(sndo_mixer_t **pmixer,
const char *playback_name,
const char *capture_name,
struct alisp_cfg *lconf)
{
return sndo_mixer_open1(pmixer, "sndo_mixer_open", "%s%s", playback_name, capture_name, lconf);
}
/**
* \brief Opens a ordinary mixer instance
* \param pmixer Returned ordinary mixer handle
* \param playback_pcm handle of the playback PCM
* \param capture_pcm handle of the capture PCM
* \param lconf Local configuration (might be NULL - use global configuration)
* \return 0 on success otherwise a negative error code
*/
int sndo_mixer_open_pcm(sndo_mixer_t **pmixer,
snd_pcm_t *playback_pcm,
snd_pcm_t *capture_pcm,
struct alisp_cfg *lconf)
{
return sndo_mixer_open1(pmixer, "sndo_mixer_open_pcm", "%ppcm%ppcm", playback_pcm, capture_pcm, lconf);
}
/**
* \brief Closes a ordinary mixer instance
* \param mixer Ordinary mixer handle to close
* \return 0 on success otherwise a negative error code
*/
int sndo_mixer_close(sndo_mixer_t *mixer)
{
int res;
res = alsa_lisp_function(mixer->alisp, NULL, "sndo_mixer_close", "n");
alsa_lisp_free(mixer->alisp);
if (mixer->_free_cfg)
alsa_lisp_default_cfg_free(mixer->cfg);
free(mixer);
return res;
}
/**
* \brief get count of poll descriptors for ordinary mixer handle
* \param mixer ordinary mixer handle
* \return count of poll descriptors
*/
int sndo_mixer_poll_descriptors_count(sndo_mixer_t *mixer)
{
int idx, err, res = -EIO;
if (mixer->hctl_count > 0) {
for (idx = 0; idx < mixer->hctl_count; idx++) {
err = snd_hctl_poll_descriptors_count(mixer->hctl[idx]);
if (err < 0)
return err;
res += err;
}
} else {
struct alisp_seq_iterator *result;
long val;
err = alsa_lisp_function(mixer->alisp, &result, "sndo_mixer_poll_descriptors_count", "n");
if (err < 0)
return err;
err = alsa_lisp_seq_integer(result, &val);
return err < 0 ? err : val;
}
return res;
}
/**
* \brief get poll descriptors
* \param mixer ordinary mixer handle
* \param pfds array of poll descriptors
* \param space space in the poll descriptor array
* \return count of filled descriptors
*/
int sndo_mixer_poll_descriptors(sndo_mixer_t *mixer, struct pollfd *pfds, unsigned int space)
{
int err, idx, res;
if (mixer->hctl_count > 0) {
for (idx = res = 0; idx < mixer->hctl_count && space > 0; idx++) {
err = snd_hctl_poll_descriptors(mixer->hctl[idx], pfds, space);
if (err < 0)
return err;
res += err;
space -= err;
}
return res;
} else {
struct alisp_seq_iterator *result;
long val;
err = alsa_lisp_function(mixer->alisp, &result, "sndo_mixer_poll_descriptors", "%i", space);
if (err < 0)
return err;
assert(0); /* FIXME: pass pfds to application */
err = alsa_lisp_seq_integer(result, &val);
return err < 0 ? err : val;
}
}
/**
* \brief get returned events from poll descriptors
* \param mixer ordinary mixer handle
* \param pfds array of poll descriptors
* \param nfds count of poll descriptors
* \param revents returned events
* \return zero if success, otherwise a negative error code
*/
int sndo_mixer_poll_descriptors_revents(sndo_mixer_t *mixer, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{
int err, idx, count, res;
if (mixer->hctl_count > 0) {
for (idx = res = 0; idx < mixer->hctl_count && nfds > 0; idx++) {
err = snd_hctl_poll_descriptors_count(mixer->hctl[idx]);
if (err < 0)
return err;
count = err;
if (nfds < (unsigned int)err)
return -EINVAL;
err = snd_hctl_poll_descriptors_revents(mixer->hctl[idx], pfds, count, revents);
if (err < 0)
return err;
if (err != count)
return -EINVAL;
pfds += count;
nfds -= err;
revents += count;
res += count;
}
return res;
} else {
struct alisp_seq_iterator *result;
long val, tmp;
assert(0); /* FIXME: pass pfds to alisp too */
err = alsa_lisp_function(mixer->alisp, &result, "sndo_mixer_poll_descriptors_revents", "%i", nfds);
if (err < 0)
return err;
err = alsa_lisp_seq_integer(result, &val);
if (err >= 0 && alsa_lisp_seq_count(result) > 1) {
alsa_lisp_seq_next(&result);
err = alsa_lisp_seq_integer(result, &tmp);
*revents = tmp;
} else {
*revents = 0;
}
return err < 0 ? err : val;
}
}
#define IOLINES 6
static const char *name_table1[] = {
"Master",
"PCM",
"Line",
"Mic"
"CD",
"AUX"
};
#define SPEAKERS 14
static const char *name_table2[] = {
"Front Left",
"Front Center Left",
"Front Center",
"Front Center Right",
"Front Right",
"Front Side Left",
"Front Side Right",
"Rear Side Left",
"Rear Side Right",
"Rear Left",
"Rear Center",
"Rear Right",
"LFE (Subwoofer)",
"Overhead"
};
#define CSOURCES 5
static const char *name_table3[] = {
"Mic",
"Line",
"CD",
"AUX",
"Mix",
};
static int compose_string(char **result, const char *s1, const char *s2, const char *s3, const char *s4)
{
int len = strlen(s1) + strlen(s2) + strlen(s3) + strlen(s4);
*result = malloc(len + 1);
if (*result == NULL)
return -ENOMEM;
strcpy(*result, s1);
strcat(*result, s2);
strcat(*result, s3);
strcat(*result, s4);
return 0;
}
/**
* \brief get ordinary mixer io control value
* \param mixer ordinary mixer handle
* \param type io type
* \param val returned value
* \return zero if success, otherwise a negative error code
*/
int sndo_mixer_io_get_name(enum sndo_mixer_io_type type, char **name)
{
if (type < IOLINES * 0x40) {
int tmp = type / 0x40;
type %= 0x40;
if (type < SPEAKERS)
return compose_string(name, name_table1[tmp], " ", name_table2[type], " Volume");
} else if (type >= SNDO_MIO_CGAIN_FL && type < SNDO_MIO_CGAIN_FL + SPEAKERS) {
return compose_string(name, "Capture Gain ", name_table2[type - SNDO_MIO_CGAIN_FL], "", "");
} else if (type >= SNDO_MIO_CSOURCE_MIC && type < SNDO_MIO_CSOURCE_MIC + CSOURCES) {
return compose_string(name, "Capture Source ", name_table3[type - SNDO_MIO_CSOURCE_MIC], "", "");
}
return -ENOENT;
}
/**
* \brief get ordinary mixer io control value
* \param mixer ordinary mixer handle
* \param type io type
* \param val returned value
* \return zero if success, otherwise a negative error code
*/
int sndo_mixer_io_get(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val)
{
struct alisp_seq_iterator *result;
long val1;
int err;
err = alsa_lisp_function(mixer->alisp, &result, "sndo_mixer_io_set", "%i", type);
if (err < 0)
return err;
err = alsa_lisp_seq_integer(result, &val1);
if (err < 0)
return err;
*val = val1;
return 0;
}
/**
* \brief set ordinary mixer io control value
* \param mixer ordinary mixer handle
* \param type io type
* \param val desired value
* \return zero if success, otherwise a negative error code
*/
int sndo_mixer_io_set(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val)
{
struct alisp_seq_iterator *result;
long val1;
int err;
err = alsa_lisp_function(mixer->alisp, &result, "sndo_mixer_io_set", "%i%i", type, *val);
if (err < 0)
return err;
err = alsa_lisp_seq_integer(result, &val1);
if (err < 0)
return err;
*val = val1;
return 0;
}
/**
* \brief try to set ordinary mixer io control value
* \param mixer ordinary mixer handle
* \param type io type
* \param val desired value
* \return zero if success, otherwise a negative error code
*
* This function does not update the value.
* It only returns the real value which will be set.
*/
int sndo_mixer_io_try_set(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val)
{
struct alisp_seq_iterator *result;
long val1;
int err;
err = alsa_lisp_function(mixer->alisp, &result, "sndo_mixer_io_try_set", "%i%i", type, *val);
if (err < 0)
return err;
err = alsa_lisp_seq_integer(result, &val1);
if (err < 0)
return err;
*val = val1;
return 0;
}
/**
* \brief get ordinary mixer io control value in dB (decibel units)
* \param mixer ordinary mixer handle
* \param type io type
* \param val returned value in dB
* \return zero if success, otherwise a negative error code
*/
int sndo_mixer_io_get_dB(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val)
{
struct alisp_seq_iterator *result;
long val1;
int err;
err = alsa_lisp_function(mixer->alisp, &result, "sndo_mixer_io_set_dB", "%i", type);
if (err < 0)
return err;
err = alsa_lisp_seq_integer(result, &val1);
if (err < 0)
return err;
*val = val1;
return 0;
}
/**
* \brief set ordinary mixer io control value in dB (decibel units)
* \param mixer ordinary mixer handle
* \param type io type
* \param val desired value in dB
* \return zero if success, otherwise a negative error code
*/
int sndo_mixer_io_set_dB(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val)
{
struct alisp_seq_iterator *result;
long val1;
int err;
err = alsa_lisp_function(mixer->alisp, &result, "sndo_mixer_io_set", "%i%i", type, *val);
if (err < 0)
return err;
err = alsa_lisp_seq_integer(result, &val1);
if (err < 0)
return err;
*val = val1;
return 0;
}
/**
* \brief try to set ordinary mixer io control value in dB (decibel units)
* \param mixer ordinary mixer handle
* \param type io type
* \param val desired and returned value in dB
* \return zero if success, otherwise a negative error code
*
* This function does not update the value.
* It only returns the real value which will be set.
*/
int sndo_mixer_io_try_set_dB(sndo_mixer_t *mixer, enum sndo_mixer_io_type type, int *val)
{
struct alisp_seq_iterator *result;
long val1;
int err;
err = alsa_lisp_function(mixer->alisp, &result, "sndo_mixer_io_try_set", "%i%i", type, *val);
if (err < 0)
return err;
err = alsa_lisp_seq_integer(result, &val1);
if (err < 0)
return err;
*val = val1;
return 0;
}
/**
* \brief get ordinary mixer io control change notification
* \param mixer ordinary mixer handle
* \param changed list of changed io types
* \param changed_array_size size of list of changed io types
* \return zero if success, otherwise a negative error code
*/
int sndo_mixer_io_change(sndo_mixer_t *mixer, enum sndo_mixer_io_type *changed, int changed_array_size)
{
struct alisp_seq_iterator *result;
long val1;
int err;
err = alsa_lisp_function(mixer->alisp, &result, "sndo_mixer_io_change", "%i", changed_array_size);
if (err < 0)
return err;
err = alsa_lisp_seq_integer(result, &val1);
if (err < 0)
return err;
if (val1 < 0)
return val1;
while (changed_array_size-- > 0) {
*changed = val1;
if (!alsa_lisp_seq_next(&result))
break;
err = alsa_lisp_seq_integer(result, &val1);
if (err < 0)
return err;
}
return 0;
}

View file

@ -1,13 +0,0 @@
#SUBDIRS =
#DIST_SUBDIRS =
EXTRA_LTLIBRARIES = libordinarypcm.la
libordinarypcm_la_SOURCES = ordinary_pcm.c
#noinst_HEADERS =
alsadir = $(datadir)/alsa
all: libordinarypcm.la
INCLUDES=-I$(top_srcdir)/include

View file

@ -1,926 +0,0 @@
/**
* \file ordinary_pcm/ordinary_pcm.c
* \ingroup PCM_ordinary
* \brief Ordinary PCM interface
* \author Jaroslav Kysela <perex@suse.cz>
* \date 2003,2004
*
* Ordinary PCM interface is a high level abtraction for
* digital audio streaming.
*
* See the \ref PCM_ordinary page for more details.
*/
/*
* Ordinary PCM Interface - main file
* Copyright (c) 2003 by Jaroslav Kysela <perex@suse.cz>
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/*! \page PCM_ordinary Ordinary PCM interface
<P>Write something here</P>
\section PCM_ordinary_overview
Write something here
*/
/**
* \example ../test/ordinary_pcm.c
* \anchor example_ordinary_pcm
*/
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdarg.h>
#include <signal.h>
#include <dlfcn.h>
#include <sys/ioctl.h>
#include <sys/poll.h>
#include <sys/shm.h>
#include <sys/mman.h>
#include <limits.h>
#include "asoundlib.h"
#include "alisp.h"
#include "pcm_ordinary.h"
struct sndo_pcm {
snd_pcm_t *playback;
snd_pcm_t *capture;
snd_pcm_hw_params_t *p_hw_params;
snd_pcm_hw_params_t *c_hw_params;
snd_pcm_sw_params_t *p_sw_params;
snd_pcm_sw_params_t *c_sw_params;
snd_pcm_t *master;
unsigned int channels;
unsigned int samplebytes;
snd_pcm_uframes_t p_offset;
snd_pcm_uframes_t c_offset;
snd_pcm_uframes_t p_period_size;
snd_pcm_uframes_t c_period_size;
snd_pcm_uframes_t transfer_block;
snd_pcm_uframes_t ring_size;
enum sndo_pcm_latency_type latency;
enum sndo_pcm_xrun_type xrun;
int setting_up;
int initialized;
};
static int sndo_pcm_setup(sndo_pcm_t *pcm);
static int sndo_pcm_initialize(sndo_pcm_t *pcm);
static inline int sndo_pcm_check_setup(sndo_pcm_t *pcm)
{
if (!pcm->initialized)
return sndo_pcm_initialize(pcm);
return 0;
}
/**
* \brief Opens a ordinary pcm instance
* \param ppcm Returned ordinary pcm handle
* \param playback_name ASCII identifier of the ordinary pcm handle (playback)
* \param capture_name ASCII identifier of the ordinary pcm handle (capture)
* \param lconf Local configuration (might be NULL - use global configuration)
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_open(sndo_pcm_t **ppcm,
const char *playback_name,
const char *capture_name,
struct alisp_cfg *lconf)
{
int err = 0;
sndo_pcm_t *pcm;
assert(ppcm);
assert(playback_name || capture_name);
*ppcm = NULL;
pcm = calloc(1, sizeof(sndo_pcm_t));
if (pcm == NULL)
return -ENOMEM;
if (playback_name) {
err = snd_pcm_hw_params_malloc(&pcm->p_hw_params);
if (err < 0)
goto __end;
err = snd_pcm_sw_params_malloc(&pcm->p_sw_params);
}
if (capture_name) {
err = snd_pcm_hw_params_malloc(&pcm->c_hw_params);
if (err < 0)
goto __end;
err = snd_pcm_sw_params_malloc(&pcm->p_sw_params);
}
if (err < 0)
goto __end;
if (lconf) {
if (playback_name) {
err = snd_pcm_open_lconf(&pcm->playback, playback_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK, NULL);
if (err < 0)
goto __end;
}
if (capture_name) {
err = snd_pcm_open_lconf(&pcm->capture, playback_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK, NULL);
if (err < 0)
goto __end;
}
} else {
if (playback_name) {
err = snd_pcm_open(&pcm->playback, playback_name, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (err < 0)
goto __end;
}
if (capture_name) {
err = snd_pcm_open(&pcm->capture, playback_name, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if (err < 0)
goto __end;
}
}
if (pcm->playback && pcm->capture) {
err = snd_pcm_link(pcm->playback, pcm->capture);
if (err < 0)
goto __end;
pcm->master = pcm->playback;
}
__end:
if (err < 0)
sndo_pcm_close(pcm);
return err;
}
/**
* \brief Closes a ordinary pcm instance
* \param pcm Ordinary pcm handle to close
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_close(sndo_pcm_t *pcm)
{
int err;
if (pcm->playback)
err = snd_pcm_close(pcm->playback);
if (pcm->capture)
err = snd_pcm_close(pcm->capture);
if (pcm->p_hw_params)
snd_pcm_hw_params_free(pcm->p_hw_params);
if (pcm->p_sw_params)
snd_pcm_sw_params_free(pcm->p_sw_params);
if (pcm->c_hw_params)
snd_pcm_hw_params_free(pcm->c_hw_params);
if (pcm->c_sw_params)
snd_pcm_sw_params_free(pcm->c_sw_params);
free(pcm);
return 0;
}
/**
* \brief get count of poll descriptors for ordinary pcm handle
* \param pcm ordinary pcm handle
* \return count of poll descriptors
*/
int sndo_pcm_poll_descriptors_count(sndo_pcm_t *pcm)
{
int err, res = 0;
err = snd_pcm_poll_descriptors_count(pcm->playback);
if (err > 0)
res += err;
err = snd_pcm_poll_descriptors_count(pcm->capture);
if (err > 0)
res += err;
return err < 0 ? err : res;
}
/**
* \brief get poll descriptors
* \param pcm ordinary pcm handle
* \param pfds array of poll descriptors
* \param space space in the poll descriptor array
* \return count of filled descriptors
*/
int sndo_pcm_poll_descriptors(sndo_pcm_t *pcm, struct pollfd *pfds, unsigned int space)
{
int playback, err, res = 0;
playback = snd_pcm_poll_descriptors_count(pcm->playback);
if (playback < 0)
return playback;
err = snd_pcm_poll_descriptors(pcm->playback, pfds, (unsigned)playback < space ? (unsigned)playback : space);
if (err < 0)
return err;
res += err;
if ((unsigned)res < space) {
err = snd_pcm_poll_descriptors(pcm->capture, pfds + res, space - res);
if (err < 0)
return err;
res += err;
}
return res;
}
/**
* \brief get returned events from poll descriptors
* \param pcm ordinary pcm handle
* \param pfds array of poll descriptors
* \param nfds count of poll descriptors
* \param revents returned events
* \return zero if success, otherwise a negative error code
*/
int sndo_pcm_poll_descriptors_revents(sndo_pcm_t *pcm, struct pollfd *pfds, unsigned int nfds, unsigned short *revents)
{
int playback, err;
unsigned short _revents;
playback = snd_pcm_poll_descriptors_count(pcm->playback);
if (playback < 0)
return playback;
err = snd_pcm_poll_descriptors_revents(pcm->playback, pfds, nfds < (unsigned)playback ? nfds : (unsigned)playback, revents);
if (err < 0)
return err;
if (nfds > (unsigned)playback) {
err = snd_pcm_poll_descriptors_revents(pcm->capture, pfds + playback, nfds - playback, &_revents);
if (err < 0)
return err;
*revents |= _revents;
}
return 0;
}
/**
* \brief Start a PCM
* \param pcm ordinary PCM handle
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_start(sndo_pcm_t *pcm)
{
int err;
err = sndo_pcm_check_setup(pcm);
if (err < 0)
return err;
/* the streams are linked, so use only one stream */
return snd_pcm_start(pcm->master);
}
/**
* \brief Stop a PCM dropping pending frames
* \param pcm ordinary PCM handle
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_drop(sndo_pcm_t *pcm)
{
/* the streams are linked, so use only one stream */
return snd_pcm_drop(pcm->master);
}
/**
* \brief Stop a PCM preserving pending frames
* \param pcm PCM handle
* \return 0 on success otherwise a negative error code
* \retval -ESTRPIPE a suspend event occurred
*
* For playback wait for all pending frames to be played and then stop
* the PCM.
* For capture stop PCM permitting to retrieve residual frames.
*/
int sndo_pcm_drain(sndo_pcm_t *pcm)
{
/* the streams are linked, so use only one stream */
return snd_pcm_drain(pcm->master);
}
/**
* \brief Obtain delay for a running PCM handle
* \param pcm ordinary PCM handle
* \param delayp Returned delay in frames
* \return 0 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.
*/
int sndo_pcm_delay(sndo_pcm_t *pcm, snd_pcm_sframes_t *delayp)
{
int err;
snd_pcm_sframes_t pdelay, cdelay;
assert(pcm);
assert(delayp);
err = sndo_pcm_check_setup(pcm);
if (err < 0)
return err;
if (pcm->playback)
err = snd_pcm_avail_update(pcm->playback);
if (err >= 0 && pcm->capture)
err = snd_pcm_avail_update(pcm->capture);
if (err >= 0 && pcm->playback)
err = snd_pcm_delay(pcm->playback, &pdelay);
if (err >= 0 && pcm->capture)
err = snd_pcm_delay(pcm->capture, &cdelay);
if (pdelay > cdelay)
pdelay = cdelay;
*delayp = pdelay;
return err;
}
/**
* \brief Obtain transfer block size (aka period size)
* \param pcm ordinary PCM handle
* \param tblock Returned transfer block size in frames
* \return 0 on success otherwise a negative error code
*
* All read/write operations must use this transfer block.
*/
int sndo_pcm_transfer_block(sndo_pcm_t *pcm, snd_pcm_uframes_t *tblock)
{
int err;
assert(pcm);
assert(tblock);
err = sndo_pcm_check_setup(pcm);
if (err < 0)
return err;
*tblock = pcm->transfer_block;
return 0;
}
/**
* \brief Resume from suspend, no samples are lost
* \param pcm ordinary PCM handle
* \return 0 on success otherwise a negative error code
* \retval -EAGAIN resume can't be proceed immediately (audio hardware is probably still suspended)
* \retval -ENOSYS hardware doesn't support this feature
*
* This function can be used when the stream is in the suspend state
* to do the fine resume from this state. Not all hardware supports
* this feature, when an -ENOSYS error is returned, use the snd_pcm_prepare
* function to recovery.
*/
int sndo_pcm_resume(sndo_pcm_t *pcm)
{
return snd_pcm_resume(pcm->master);
}
/**
* \brief Wait for a PCM to become ready
* \param pcm ordinary PCM handle
* \param timeout maximum time in milliseconds to wait
* \return a positive value on success otherwise a negative error code
* \retval 0 timeout occurred
* \retval 1 PCM stream is ready for I/O
*/
int sndo_pcm_wait(sndo_pcm_t *pcm, int timeout)
{
struct pollfd pfd[2];
unsigned short p_revents, c_revents;
int err;
err = snd_pcm_poll_descriptors(pcm->playback, &pfd[0], 1);
assert(err == 1);
err = snd_pcm_poll_descriptors(pcm->capture, &pfd[1], 1);
assert(err == 1);
err = poll(pfd, 2, timeout);
if (err < 0)
return -errno;
if (err == 0)
return 0;
do {
err = snd_pcm_poll_descriptors_revents(pcm->playback, &pfd[0], 1, &p_revents);
if (err < 0)
return err;
if (p_revents & (POLLERR | POLLNVAL))
return -EIO;
err = snd_pcm_poll_descriptors_revents(pcm->capture, &pfd[1], 1, &c_revents);
if (err < 0)
return err;
if (c_revents & (POLLERR | POLLNVAL))
return -EIO;
if ((p_revents & POLLOUT) && (c_revents & POLLIN))
return 1;
err = poll(&pfd[(p_revents & POLLOUT) ? 1 : 0], 1, 1);
if (err < 0)
return err;
} while (1);
}
/**
* \brief Get raw (lowlevel) playback PCM handle
* \param pcm ordinary PCM handle
* \return raw (lowlevel) capture PCM handle or NULL
*/
snd_pcm_t *sndo_pcm_raw_playback(sndo_pcm_t *pcm)
{
return pcm->playback;
}
/**
* \brief Get raw (lowlevel) capture PCM handle
* \param pcm ordinary PCM handle
* \return raw (lowlevel) capture PCM handle or NULL
*/
snd_pcm_t *sndo_pcm_raw_capture(sndo_pcm_t *pcm)
{
return pcm->capture;
}
/**
* \brief Reset all parameters
* \param pcm ordinary PCM handle
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_param_reset(sndo_pcm_t *pcm)
{
int err;
err = sndo_pcm_drain(pcm);
if (err < 0)
return err;
pcm->initialized = 0;
if (pcm->playback) {
err = snd_pcm_hw_params_any(pcm->playback, pcm->p_hw_params);
if (err < 0)
return err;
err = snd_pcm_sw_params_current(pcm->playback, pcm->p_sw_params);
if (err < 0)
return err;
}
if (pcm->capture) {
err = snd_pcm_hw_params_any(pcm->capture, pcm->c_hw_params);
if (err < 0)
return err;
err = snd_pcm_sw_params_current(pcm->capture, pcm->c_sw_params);
if (err < 0)
return err;
}
return 0;
}
/**
* \brief Set sample access type
* \param pcm ordinary PCM handle
* \param access access type (interleaved or noninterleaved)
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_param_access(sndo_pcm_t *pcm, enum sndo_pcm_access_type access)
{
int err;
snd_pcm_access_t native_access = SND_PCM_ACCESS_MMAP_INTERLEAVED;
switch (access) {
case SNDO_PCM_ACCESS_INTERLEAVED: native_access = SND_PCM_ACCESS_MMAP_INTERLEAVED; break;
case SNDO_PCM_ACCESS_NONINTERLEAVED: native_access = SND_PCM_ACCESS_MMAP_NONINTERLEAVED; break;
default:
return -EINVAL;
}
err = sndo_pcm_setup(pcm);
if (err < 0)
return err;
if (pcm->playback) {
err = snd_pcm_hw_params_set_access(pcm->playback, pcm->p_hw_params, native_access);
if (err < 0) {
SNDERR("cannot set requested access for the playback direction");
return err;
}
}
if (pcm->capture) {
err = snd_pcm_hw_params_set_access(pcm->capture, pcm->c_hw_params, native_access);
if (err < 0) {
SNDERR("cannot set requested access for the capture direction");
return err;
}
}
return 0;
}
/**
* \brief Set stream rate
* \param pcm ordinary PCM handle
* \param rate requested rate
* \param used_rate returned real rate
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_param_rate(sndo_pcm_t *pcm, unsigned int rate, unsigned int *used_rate)
{
int err;
unsigned int prate = rate, crate = rate;
err = sndo_pcm_setup(pcm);
if (err < 0)
return err;
if (pcm->playback) {
err = snd_pcm_hw_params_set_rate_near(pcm->playback, pcm->p_hw_params, &prate, 0);
if (err < 0) {
SNDERR("cannot set requested rate for the playback direction");
return err;
}
}
if (pcm->capture) {
err = snd_pcm_hw_params_set_rate_near(pcm->capture, pcm->c_hw_params, &crate, 0);
if (err < 0) {
SNDERR("cannot set requested rate for the capture direction");
return err;
}
}
if (used_rate)
*used_rate = pcm->capture ? crate : prate;
return 0;
}
/**
* \brief Set channels in stream
* \param pcm ordinary PCM handle
* \param channels requested channels
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_param_channels(sndo_pcm_t *pcm, unsigned int channels)
{
int err;
err = sndo_pcm_setup(pcm);
if (err < 0)
return err;
if (pcm->playback) {
err = snd_pcm_hw_params_set_channels(pcm->capture, pcm->p_hw_params, channels);
if (err < 0) {
SNDERR("cannot set requested channels for the playback direction");
return err;
}
}
if (pcm->capture) {
err = snd_pcm_hw_params_set_channels(pcm->capture, pcm->c_hw_params, channels);
if (err < 0) {
SNDERR("cannot set requested channels for the capture direction");
return err;
}
}
return 0;
}
/**
* \brief Set stream format
* \param pcm ordinary PCM handle
* \param rate requested channels
* \param used_rate returned real channels
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_param_format(sndo_pcm_t *pcm, snd_pcm_format_t format, snd_pcm_subformat_t subformat)
{
int err;
if (subformat != SND_PCM_SUBFORMAT_STD)
return -EINVAL;
err = snd_pcm_format_physical_width(format);
if (err < 0)
return err;
if (err % 8)
return -EINVAL;
err = sndo_pcm_setup(pcm);
if (err < 0)
return err;
if (pcm->playback) {
err = snd_pcm_hw_params_set_format(pcm->capture, pcm->p_hw_params, format);
if (err < 0) {
SNDERR("cannot set requested format for the playback direction");
return err;
}
}
if (pcm->capture) {
err = snd_pcm_hw_params_set_format(pcm->capture, pcm->c_hw_params, format);
if (err < 0) {
SNDERR("cannot set requested format for the capture direction");
return err;
}
}
return 0;
}
/**
* \brief Set stream latency
* \param pcm ordinary PCM handle
* \param latency requested latency
* \param used_latency returned real latency in frames
* \return 0 on success otherwise a negative error code
*
* Note that the result value is only approximate and for one direction.
* For example, hardware FIFOs are not counted etc.
*/
int sndo_pcm_param_latency(sndo_pcm_t *pcm, enum sndo_pcm_latency_type latency, snd_pcm_uframes_t *used_latency)
{
int err;
err = sndo_pcm_setup(pcm);
if (err < 0)
return err;
pcm->latency = latency;
err = sndo_pcm_check_setup(pcm);
if (err < 0)
return err;
if (used_latency)
*used_latency = pcm->ring_size;
return 0;
}
/**
* \brief Set xrun behaviour
* \param pcm ordinary PCM handle
* \param xrun requested behaviour
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_param_xrun(sndo_pcm_t *pcm, enum sndo_pcm_xrun_type xrun)
{
int err;
err = sndo_pcm_setup(pcm);
if (err < 0)
return err;
pcm->xrun = xrun;
return 0;
}
/**
* \brief Begin the playback interleaved frame update
* \param pcm ordinary PCM handle
* \param ring_buffer returned pointer to actual destination area
* \param frames returned maximum count of updated frames
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_pio_ibegin(sndo_pcm_t *pcm, void **ring_buffer, snd_pcm_uframes_t *frames)
{
int err;
const snd_pcm_channel_area_t *areas;
err = sndo_pcm_check_setup(pcm);
if (err < 0)
return err;
err = snd_pcm_mmap_begin(pcm->playback, &areas, &pcm->p_offset, frames);
if (err < 0)
return err;
if (*frames < pcm->transfer_block) {
frames = 0;
} else {
*frames -= *frames % pcm->transfer_block;
*ring_buffer = (char *)areas->addr + (areas->first / 8) + pcm->p_offset * pcm->samplebytes;
}
return 0;
}
/**
* \brief Finish the playback interleave frame update (commit data to hardware)
* \param pcm ordinary PCM handle
* \param frames count of updated frames
* \return count of transferred frames on success otherwise a negative error code
*/
snd_pcm_sframes_t sndo_pcm_pio_iend(sndo_pcm_t *pcm, snd_pcm_uframes_t frames)
{
if (frames <= 0)
return -EINVAL;
if (frames % pcm->transfer_block)
return -EINVAL;
return snd_pcm_mmap_commit(pcm->playback, pcm->p_offset, frames);
}
/**
* \brief Begin the playback noninterleaved frame update
* \param pcm ordinary PCM handle
* \param ring_buffer returned pointer to actual destination area
* \param frames returned maximum count of updated frames
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_pio_nbegin(sndo_pcm_t *pcm, void ***ring_buffer, snd_pcm_uframes_t *frames)
{
int err;
unsigned ch;
const snd_pcm_channel_area_t *areas;
err = sndo_pcm_check_setup(pcm);
if (err < 0)
return err;
err = snd_pcm_mmap_begin(pcm->playback, &areas, &pcm->p_offset, frames);
if (err < 0)
return err;
if (*frames < pcm->transfer_block) {
frames = 0;
} else {
*frames -= *frames % pcm->transfer_block;
for (ch = 0; ch < pcm->channels; ch++)
ring_buffer[ch] = areas->addr + (areas->first / 8) + pcm->p_offset * pcm->samplebytes;
}
return 0;
}
/**
* \brief Finish the playback noninterleave frame update (commit data to hardware)
* \param pcm ordinary PCM handle
* \param frames count of updated frames
* \return count of transferred frames on success otherwise a negative error code
*/
snd_pcm_sframes_t sndo_pcm_pio_nend(sndo_pcm_t *pcm, snd_pcm_uframes_t frames)
{
if (frames <= 0)
return -EINVAL;
if (frames % pcm->transfer_block)
return -EINVAL;
return snd_pcm_mmap_commit(pcm->playback, pcm->p_offset, frames);
}
/**
* \brief Begin the capture interleaved frame update
* \param pcm ordinary PCM handle
* \param ring_buffer returned pointer to actual destination area
* \param frames returned maximum count of updated frames
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_cio_ibegin(sndo_pcm_t *pcm, void **ring_buffer, snd_pcm_uframes_t *frames)
{
int err;
const snd_pcm_channel_area_t *areas;
err = sndo_pcm_check_setup(pcm);
if (err < 0)
return err;
err = snd_pcm_mmap_begin(pcm->capture, &areas, &pcm->c_offset, frames);
if (err < 0)
return err;
if (*frames < pcm->transfer_block) {
frames = 0;
} else {
*frames -= *frames % pcm->transfer_block;
*ring_buffer = (char *)areas->addr + (areas->first / 8) + pcm->c_offset * pcm->samplebytes;
}
return 0;
}
/**
* \brief Finish the capture interleave frame update (commit data to hardware)
* \param pcm ordinary PCM handle
* \param frames count of updated frames
* \return count of transferred frames on success otherwise a negative error code
*/
snd_pcm_sframes_t sndo_pcm_cio_iend(sndo_pcm_t *pcm, snd_pcm_uframes_t frames)
{
if (frames <= 0)
return -EINVAL;
if (frames % pcm->transfer_block)
return -EINVAL;
return snd_pcm_mmap_commit(pcm->capture, pcm->p_offset, frames);
}
/**
* \brief Begin the capture noninterleaved frame update
* \param pcm ordinary PCM handle
* \param ring_buffer returned pointer to actual destination area
* \param frames returned maximum count of updated frames
* \return 0 on success otherwise a negative error code
*/
int sndo_pcm_cio_nbegin(sndo_pcm_t *pcm, void ***ring_buffer, snd_pcm_uframes_t *frames)
{
int err;
unsigned ch;
const snd_pcm_channel_area_t *areas;
err = sndo_pcm_check_setup(pcm);
if (err < 0)
return err;
err = snd_pcm_mmap_begin(pcm->capture, &areas, &pcm->c_offset, frames);
if (err < 0)
return err;
if (*frames < pcm->transfer_block) {
frames = 0;
} else {
*frames -= *frames % pcm->transfer_block;
for (ch = 0; ch < pcm->channels; ch++)
ring_buffer[ch] = areas->addr + (areas->first / 8) + pcm->c_offset * pcm->samplebytes;
}
return 0;
}
/**
* \brief Finish the capture noninterleave frame update (commit data to hardware)
* \param pcm ordinary PCM handle
* \param frames count of updated frames
* \return count of transferred frames on success otherwise a negative error code
*/
snd_pcm_sframes_t sndo_pcm_cio_nend(sndo_pcm_t *pcm, snd_pcm_uframes_t frames)
{
if (frames <= 0)
return -EINVAL;
if (frames % pcm->transfer_block)
return -EINVAL;
return snd_pcm_mmap_commit(pcm->capture, pcm->c_offset, frames);
}
/*
* helpers
*/
static int sndo_pcm_setup(sndo_pcm_t *pcm)
{
int err;
err = sndo_pcm_drain(pcm);
if (err < 0)
return err;
if (!pcm->setting_up) {
int err = sndo_pcm_param_reset(pcm);
if (err < 0)
return err;
pcm->setting_up = 1;
}
return 0;
}
static int sndo_pcm_initialize(sndo_pcm_t *pcm)
{
int err;
snd_pcm_uframes_t boundary;
snd_pcm_uframes_t p_period_size = ~0UL, c_period_size = ~0UL;
snd_pcm_uframes_t p_buffer_size = ~0UL, c_buffer_size = ~0UL;
if (pcm->playback) {
err = snd_pcm_hw_params(pcm->playback, pcm->p_hw_params);
if (err < 0)
return err;
err = snd_pcm_hw_params_get_period_size(pcm->p_hw_params, &p_period_size, NULL);
if (err < 0)
return err;
err = snd_pcm_hw_params_get_buffer_size(pcm->p_hw_params, &p_buffer_size);
if (err < 0)
return err;
}
if (pcm->capture) {
err = snd_pcm_hw_params(pcm->capture, pcm->c_hw_params);
if (err < 0)
return err;
err = snd_pcm_hw_params_get_period_size(pcm->c_hw_params, &c_period_size, NULL);
if (err < 0)
return err;
err = snd_pcm_hw_params_get_buffer_size(pcm->c_hw_params, &c_buffer_size);
if (err < 0)
return err;
}
if (p_period_size < c_period_size)
pcm->transfer_block = p_period_size;
else
pcm->transfer_block = c_period_size;
if (p_buffer_size < c_buffer_size)
pcm->ring_size = p_buffer_size;
else
pcm->ring_size = c_buffer_size;
if (pcm->playback) {
err = snd_pcm_sw_params_get_boundary(pcm->p_sw_params, &boundary);
if (err < 0)
return err;
err = snd_pcm_sw_params_set_start_threshold(pcm->playback, pcm->p_sw_params, boundary);
if (err < 0)
return err;
err = snd_pcm_sw_params_set_stop_threshold(pcm->playback, pcm->p_sw_params, pcm->xrun == SNDO_PCM_XRUN_IGNORE ? boundary : pcm->ring_size);
if (err < 0)
return err;
err = snd_pcm_sw_params_set_xfer_align(pcm->playback, pcm->p_sw_params, 1);
if (err < 0)
return err;
err = snd_pcm_sw_params_set_avail_min(pcm->playback, pcm->p_sw_params, pcm->transfer_block);
if (err < 0)
return err;
err = snd_pcm_sw_params(pcm->playback, pcm->p_sw_params);
if (err < 0)
return err;
}
if (pcm->capture) {
err = snd_pcm_sw_params_get_boundary(pcm->c_sw_params, &boundary);
if (err < 0)
return err;
err = snd_pcm_sw_params_set_start_threshold(pcm->capture, pcm->c_sw_params, boundary);
if (err < 0)
return err;
err = snd_pcm_sw_params_set_stop_threshold(pcm->capture, pcm->c_sw_params, pcm->xrun == SNDO_PCM_XRUN_IGNORE ? boundary : pcm->ring_size);
if (err < 0)
return err;
err = snd_pcm_sw_params_set_xfer_align(pcm->capture, pcm->c_sw_params, 1);
if (err < 0)
return err;
err = snd_pcm_sw_params_set_avail_min(pcm->capture, pcm->c_sw_params, pcm->transfer_block);
if (err < 0)
return err;
err = snd_pcm_sw_params(pcm->capture, pcm->c_sw_params);
if (err < 0)
return err;
}
pcm->initialized = 1;
return 0;
}