Blame src/pcm/pcm_simple.c

Packit Service db8eaa
/**
Packit Service db8eaa
 * \file pcm/pcm_simple.c
Packit Service db8eaa
 * \ingroup PCM_Simple
Packit Service db8eaa
 * \brief PCM Simple Interface
Packit Service db8eaa
 * \author Jaroslav Kysela <perex@perex.cz>
Packit Service db8eaa
 * \date 2004
Packit Service db8eaa
 */
Packit Service db8eaa
/*
Packit Service db8eaa
 *
Packit Service db8eaa
 *   This library is free software; you can redistribute it and/or modify
Packit Service db8eaa
 *   it under the terms of the GNU Lesser General Public License as
Packit Service db8eaa
 *   published by the Free Software Foundation; either version 2.1 of
Packit Service db8eaa
 *   the License, or (at your option) any later version.
Packit Service db8eaa
 *
Packit Service db8eaa
 *   This program is distributed in the hope that it will be useful,
Packit Service db8eaa
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service db8eaa
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service db8eaa
 *   GNU Lesser General Public License for more details.
Packit Service db8eaa
 *
Packit Service db8eaa
 *   You should have received a copy of the GNU Lesser General Public
Packit Service db8eaa
 *   License along with this library; if not, write to the Free Software
Packit Service db8eaa
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
Packit Service db8eaa
 *
Packit Service db8eaa
 */
Packit Service db8eaa
Packit Service db8eaa
#include "pcm_local.h"
Packit Service db8eaa
Packit Service db8eaa
static int set_buffer_time(snd_spcm_latency_t latency,
Packit Service db8eaa
			   unsigned int *buffer_time)
Packit Service db8eaa
{
Packit Service db8eaa
	switch (latency) {
Packit Service db8eaa
	case SND_SPCM_LATENCY_STANDARD:
Packit Service db8eaa
		*buffer_time = 350000;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_SPCM_LATENCY_MEDIUM:
Packit Service db8eaa
		*buffer_time = 25000;
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_SPCM_LATENCY_REALTIME:
Packit Service db8eaa
		*buffer_time = 2500;
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
static int set_hw_params(snd_pcm_t *pcm,
Packit Service db8eaa
			 snd_pcm_hw_params_t *hw_params,
Packit Service db8eaa
			 unsigned int *rate,
Packit Service db8eaa
			 unsigned int channels,
Packit Service db8eaa
			 snd_pcm_format_t format,
Packit Service db8eaa
			 snd_pcm_subformat_t subformat,
Packit Service db8eaa
			 unsigned int *buffer_time,
Packit Service db8eaa
			 unsigned int *period_time,
Packit Service db8eaa
			 snd_pcm_access_t access)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	/*
Packit Service db8eaa
	 * hardware parameters
Packit Service db8eaa
	 */	
Packit Service db8eaa
	err = snd_pcm_hw_params_any(pcm, hw_params);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = snd_pcm_hw_params_set_access(pcm, hw_params, access);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = snd_pcm_hw_params_set_format(pcm, hw_params, format);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	if (subformat != SND_PCM_SUBFORMAT_STD) {
Packit Service db8eaa
		err = snd_pcm_hw_params_set_subformat(pcm, hw_params, subformat);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	err = snd_pcm_hw_params_set_channels(pcm, hw_params, channels);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = INTERNAL(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, 0);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(pcm, hw_params, buffer_time, NULL);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	if (period_time == NULL || *period_time == 0) {
Packit Service db8eaa
		unsigned int periods = 3;
Packit Service db8eaa
		err = INTERNAL(snd_pcm_hw_params_set_periods_near)(pcm, hw_params, &periods, NULL);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		if (periods == 1)
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
		if (period_time) {
Packit Service db8eaa
			err = INTERNAL(snd_pcm_hw_params_get_period_time)(hw_params, period_time, NULL);
Packit Service db8eaa
			if (err < 0)
Packit Service db8eaa
				return err;
Packit Service db8eaa
		}			
Packit Service db8eaa
	} else {
Packit Service db8eaa
		err = snd_pcm_hw_params_set_period_time(pcm, hw_params, *period_time, 0);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
		if (*buffer_time == *period_time)
Packit Service db8eaa
			return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	err = snd_pcm_hw_params(pcm, hw_params);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}		
Packit Service db8eaa
Packit Service db8eaa
static int set_sw_params(snd_pcm_t *pcm,
Packit Service db8eaa
			 snd_pcm_sw_params_t *sw_params,
Packit Service db8eaa
		         snd_spcm_xrun_type_t xrun_type)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
Packit Service db8eaa
	err = snd_pcm_sw_params_current(pcm, sw_params);		
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = snd_pcm_sw_params_set_start_threshold(pcm, sw_params, (pcm->buffer_size / pcm->period_size) * pcm->period_size);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = snd_pcm_sw_params_set_avail_min(pcm, sw_params, pcm->period_size);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	switch (xrun_type) {
Packit Service db8eaa
	case SND_SPCM_XRUN_STOP:
Packit Service db8eaa
		err = snd_pcm_sw_params_set_stop_threshold(pcm, sw_params, pcm->buffer_size);
Packit Service db8eaa
		break;
Packit Service db8eaa
	case SND_SPCM_XRUN_IGNORE:
Packit Service db8eaa
		err = snd_pcm_sw_params_set_stop_threshold(pcm, sw_params, pcm->boundary);
Packit Service db8eaa
		break;
Packit Service db8eaa
	default:
Packit Service db8eaa
		return -EINVAL;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = snd_pcm_sw_params(pcm, sw_params);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/**
Packit Service db8eaa
 * \brief Set up a simple PCM
Packit Service db8eaa
 * \param pcm PCM handle
Packit Service db8eaa
 * \param rate Sample rate
Packit Service db8eaa
 * \param channels Number of channels
Packit Service db8eaa
 * \param format PCM format
Packit Service db8eaa
 * \param subformat PCM subformat
Packit Service db8eaa
 * \param latency Latency type
Packit Service db8eaa
 * \param access PCM acceess type
Packit Service db8eaa
 * \param xrun_type XRUN type
Packit Service db8eaa
 * \return 0 if successful, or a negative error code
Packit Service db8eaa
 *
Packit Service db8eaa
 * \warning The simple PCM API may be broken in the current release.
Packit Service db8eaa
 */
Packit Service db8eaa
int snd_spcm_init(snd_pcm_t *pcm,
Packit Service db8eaa
		  unsigned int rate,
Packit Service db8eaa
		  unsigned int channels,
Packit Service db8eaa
		  snd_pcm_format_t format,
Packit Service db8eaa
		  snd_pcm_subformat_t subformat,
Packit Service db8eaa
		  snd_spcm_latency_t latency,
Packit Service db8eaa
		  snd_pcm_access_t access,
Packit Service db8eaa
		  snd_spcm_xrun_type_t xrun_type)
Packit Service db8eaa
{
Packit Service db8eaa
	int err;
Packit Service db8eaa
	snd_pcm_hw_params_t hw_params = {0};
Packit Service db8eaa
	snd_pcm_sw_params_t sw_params = {0};
Packit Service db8eaa
	unsigned int rrate;
Packit Service db8eaa
	unsigned int buffer_time;
Packit Service db8eaa
Packit Service db8eaa
	assert(pcm);
Packit Service db8eaa
	assert(rate >= 5000 && rate <= 786000);
Packit Service db8eaa
	assert(channels >= 1 && channels <= 512);
Packit Service db8eaa
Packit Service db8eaa
	rrate = rate;
Packit Service db8eaa
	err = set_buffer_time(latency, &buffer_time);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	err = set_hw_params(pcm, &hw_params,
Packit Service db8eaa
			    &rrate, channels, format, subformat,
Packit Service db8eaa
			    &buffer_time, NULL, access);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	err = set_sw_params(pcm, &sw_params, xrun_type);
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/**
Packit Service db8eaa
 * \brief Initialize simple PCMs in the duplex mode
Packit Service db8eaa
 * \param playback_pcm PCM handle for playback
Packit Service db8eaa
 * \param capture_pcm PCM handle for capture
Packit Service db8eaa
 * \param rate Sample rate
Packit Service db8eaa
 * \param channels Number of channels
Packit Service db8eaa
 * \param format PCM format
Packit Service db8eaa
 * \param subformat PCM subformat
Packit Service db8eaa
 * \param latency Latency type
Packit Service db8eaa
 * \param access PCM acceess type
Packit Service db8eaa
 * \param xrun_type XRUN type
Packit Service db8eaa
 * \param duplex_type Duplex mode
Packit Service db8eaa
 * \return 0 if successful, or a negative error code
Packit Service db8eaa
 *
Packit Service db8eaa
 * \warning The simple PCM API may be broken in the current release.
Packit Service db8eaa
 */
Packit Service db8eaa
int snd_spcm_init_duplex(snd_pcm_t *playback_pcm,
Packit Service db8eaa
			 snd_pcm_t *capture_pcm,
Packit Service db8eaa
			 unsigned int rate,
Packit Service db8eaa
			 unsigned int channels,
Packit Service db8eaa
			 snd_pcm_format_t format,
Packit Service db8eaa
			 snd_pcm_subformat_t subformat,
Packit Service db8eaa
			 snd_spcm_latency_t latency,
Packit Service db8eaa
			 snd_pcm_access_t access,
Packit Service db8eaa
			 snd_spcm_xrun_type_t xrun_type,
Packit Service db8eaa
			 snd_spcm_duplex_type_t duplex_type)
Packit Service db8eaa
{
Packit Service db8eaa
	int err, i;
Packit Service db8eaa
	snd_pcm_hw_params_t hw_params = {0};
Packit Service db8eaa
	snd_pcm_sw_params_t sw_params = {0};
Packit Service db8eaa
	unsigned int rrate;
Packit Service db8eaa
	unsigned int xbuffer_time, buffer_time[2];
Packit Service db8eaa
	unsigned int period_time[2];
Packit Service db8eaa
	snd_pcm_t *pcms[2];
Packit Service db8eaa
Packit Service db8eaa
	assert(playback_pcm);
Packit Service db8eaa
	assert(capture_pcm);
Packit Service db8eaa
	assert(rate >= 5000 && rate <= 768000);
Packit Service db8eaa
	assert(channels >= 1 && channels <= 512);
Packit Service db8eaa
Packit Service db8eaa
	pcms[0] = playback_pcm;
Packit Service db8eaa
	pcms[1] = capture_pcm;
Packit Service db8eaa
Packit Service db8eaa
	/*
Packit Service db8eaa
	 * hardware parameters
Packit Service db8eaa
	 */
Packit Service db8eaa
	err = set_buffer_time(latency, &xbuffer_time);	
Packit Service db8eaa
	if (err < 0)
Packit Service db8eaa
		return err;
Packit Service db8eaa
	
Packit Service db8eaa
	for (i = 0; i < 2; i++) {
Packit Service db8eaa
		buffer_time[i] = xbuffer_time;
Packit Service db8eaa
		period_time[i] = i > 0 ? period_time[0] : 0;
Packit Service db8eaa
		rrate = rate;
Packit Service db8eaa
		err = set_hw_params(pcms[i], &hw_params,
Packit Service db8eaa
				    &rrate, channels, format, subformat,
Packit Service db8eaa
				    &buffer_time[i], &period_time[i], access);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
	if (buffer_time[0] == buffer_time[1] &&
Packit Service db8eaa
	    period_time[0] == period_time[1])
Packit Service db8eaa
		goto __sw_params;
Packit Service db8eaa
	if (duplex_type == SND_SPCM_DUPLEX_LIBERAL)
Packit Service db8eaa
		goto __sw_params;
Packit Service db8eaa
	/* FIXME: */
Packit Service db8eaa
	return -EINVAL;
Packit Service db8eaa
Packit Service db8eaa
	/*
Packit Service db8eaa
	 * software parameters
Packit Service db8eaa
	 */
Packit Service db8eaa
      __sw_params:
Packit Service db8eaa
	for (i = 0; i < 2; i++) {
Packit Service db8eaa
		err = set_sw_params(pcms[i], &sw_params, xrun_type);
Packit Service db8eaa
		if (err < 0)
Packit Service db8eaa
			return err;
Packit Service db8eaa
	}
Packit Service db8eaa
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}
Packit Service db8eaa
Packit Service db8eaa
/**
Packit Service db8eaa
 * \brief Get the set up of simple PCM
Packit Service db8eaa
 * \param pcm PCM handle
Packit Service db8eaa
 * \param rate Pointer to store the current sample rate
Packit Service db8eaa
 * \param buffer_size Pointer to store the current buffer size
Packit Service db8eaa
 * \param period_size Pointer to store the current period size
Packit Service db8eaa
 * \return 0 if successful, or a negative error code
Packit Service db8eaa
 *
Packit Service db8eaa
 * \warning The simple PCM API may be broken in the current release.
Packit Service db8eaa
 */
Packit Service db8eaa
int snd_spcm_init_get_params(snd_pcm_t *pcm,
Packit Service db8eaa
			     unsigned int *rate,
Packit Service db8eaa
			     snd_pcm_uframes_t *buffer_size,
Packit Service db8eaa
			     snd_pcm_uframes_t *period_size)
Packit Service db8eaa
{
Packit Service db8eaa
	assert(pcm);
Packit Service db8eaa
	if (!pcm->setup)
Packit Service db8eaa
		return -EBADFD;
Packit Service db8eaa
	if (rate)
Packit Service db8eaa
		*rate = pcm->rate;
Packit Service db8eaa
	if (buffer_size)
Packit Service db8eaa
		*buffer_size = pcm->buffer_size;
Packit Service db8eaa
	if (period_size)
Packit Service db8eaa
		*period_size = pcm->period_size;
Packit Service db8eaa
	return 0;
Packit Service db8eaa
}