| /** | |
| * \file pcm/pcm_simple.c | |
| * \ingroup PCM_Simple | |
| * \brief PCM Simple Interface | |
| * \author Jaroslav Kysela <perex@perex.cz> | |
| * \date 2004 | |
| */ | |
| /* | |
| * | |
| * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
| * | |
| */ | |
| #include "pcm_local.h" | |
| static int set_buffer_time(snd_spcm_latency_t latency, | |
| unsigned int *buffer_time) | |
| { | |
| switch (latency) { | |
| case SND_SPCM_LATENCY_STANDARD: | |
| *buffer_time = 350000; | |
| break; | |
| case SND_SPCM_LATENCY_MEDIUM: | |
| *buffer_time = 25000; | |
| break; | |
| case SND_SPCM_LATENCY_REALTIME: | |
| *buffer_time = 2500; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| return 0; | |
| } | |
| static int set_hw_params(snd_pcm_t *pcm, | |
| snd_pcm_hw_params_t *hw_params, | |
| unsigned int *rate, | |
| unsigned int channels, | |
| snd_pcm_format_t format, | |
| snd_pcm_subformat_t subformat, | |
| unsigned int *buffer_time, | |
| unsigned int *period_time, | |
| snd_pcm_access_t access) | |
| { | |
| int err; | |
| /* | |
| * hardware parameters | |
| */ | |
| err = snd_pcm_hw_params_any(pcm, hw_params); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_hw_params_set_access(pcm, hw_params, access); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_hw_params_set_format(pcm, hw_params, format); | |
| if (err < 0) | |
| return err; | |
| if (subformat != SND_PCM_SUBFORMAT_STD) { | |
| err = snd_pcm_hw_params_set_subformat(pcm, hw_params, subformat); | |
| if (err < 0) | |
| return err; | |
| } | |
| err = snd_pcm_hw_params_set_channels(pcm, hw_params, channels); | |
| if (err < 0) | |
| return err; | |
| err = INTERNAL(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, 0); | |
| if (err < 0) | |
| return err; | |
| err = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(pcm, hw_params, buffer_time, NULL); | |
| if (err < 0) | |
| return err; | |
| if (period_time == NULL || *period_time == 0) { | |
| unsigned int periods = 3; | |
| err = INTERNAL(snd_pcm_hw_params_set_periods_near)(pcm, hw_params, &periods, NULL); | |
| if (err < 0) | |
| return err; | |
| if (periods == 1) | |
| return -EINVAL; | |
| if (period_time) { | |
| err = INTERNAL(snd_pcm_hw_params_get_period_time)(hw_params, period_time, NULL); | |
| if (err < 0) | |
| return err; | |
| } | |
| } else { | |
| err = snd_pcm_hw_params_set_period_time(pcm, hw_params, *period_time, 0); | |
| if (err < 0) | |
| return err; | |
| if (*buffer_time == *period_time) | |
| return -EINVAL; | |
| } | |
| err = snd_pcm_hw_params(pcm, hw_params); | |
| if (err < 0) | |
| return err; | |
| return 0; | |
| } | |
| static int set_sw_params(snd_pcm_t *pcm, | |
| snd_pcm_sw_params_t *sw_params, | |
| snd_spcm_xrun_type_t xrun_type) | |
| { | |
| int err; | |
| err = snd_pcm_sw_params_current(pcm, sw_params); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_sw_params_set_start_threshold(pcm, sw_params, (pcm->buffer_size / pcm->period_size) * pcm->period_size); | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_sw_params_set_avail_min(pcm, sw_params, pcm->period_size); | |
| if (err < 0) | |
| return err; | |
| switch (xrun_type) { | |
| case SND_SPCM_XRUN_STOP: | |
| err = snd_pcm_sw_params_set_stop_threshold(pcm, sw_params, pcm->buffer_size); | |
| break; | |
| case SND_SPCM_XRUN_IGNORE: | |
| err = snd_pcm_sw_params_set_stop_threshold(pcm, sw_params, pcm->boundary); | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| if (err < 0) | |
| return err; | |
| err = snd_pcm_sw_params(pcm, sw_params); | |
| if (err < 0) | |
| return err; | |
| return 0; | |
| } | |
| /** | |
| * \brief Set up a simple PCM | |
| * \param pcm PCM handle | |
| * \param rate Sample rate | |
| * \param channels Number of channels | |
| * \param format PCM format | |
| * \param subformat PCM subformat | |
| * \param latency Latency type | |
| * \param access PCM acceess type | |
| * \param xrun_type XRUN type | |
| * \return 0 if successful, or a negative error code | |
| * | |
| * \warning The simple PCM API may be broken in the current release. | |
| */ | |
| int snd_spcm_init(snd_pcm_t *pcm, | |
| unsigned int rate, | |
| unsigned int channels, | |
| snd_pcm_format_t format, | |
| snd_pcm_subformat_t subformat, | |
| snd_spcm_latency_t latency, | |
| snd_pcm_access_t access, | |
| snd_spcm_xrun_type_t xrun_type) | |
| { | |
| int err; | |
| snd_pcm_hw_params_t hw_params = {0}; | |
| snd_pcm_sw_params_t sw_params = {0}; | |
| unsigned int rrate; | |
| unsigned int buffer_time; | |
| assert(pcm); | |
| assert(rate >= 5000 && rate <= 786000); | |
| assert(channels >= 1 && channels <= 512); | |
| rrate = rate; | |
| err = set_buffer_time(latency, &buffer_time); | |
| if (err < 0) | |
| return err; | |
| err = set_hw_params(pcm, &hw_params, | |
| &rrate, channels, format, subformat, | |
| &buffer_time, NULL, access); | |
| if (err < 0) | |
| return err; | |
| err = set_sw_params(pcm, &sw_params, xrun_type); | |
| if (err < 0) | |
| return err; | |
| return 0; | |
| } | |
| /** | |
| * \brief Initialize simple PCMs in the duplex mode | |
| * \param playback_pcm PCM handle for playback | |
| * \param capture_pcm PCM handle for capture | |
| * \param rate Sample rate | |
| * \param channels Number of channels | |
| * \param format PCM format | |
| * \param subformat PCM subformat | |
| * \param latency Latency type | |
| * \param access PCM acceess type | |
| * \param xrun_type XRUN type | |
| * \param duplex_type Duplex mode | |
| * \return 0 if successful, or a negative error code | |
| * | |
| * \warning The simple PCM API may be broken in the current release. | |
| */ | |
| int snd_spcm_init_duplex(snd_pcm_t *playback_pcm, | |
| snd_pcm_t *capture_pcm, | |
| unsigned int rate, | |
| unsigned int channels, | |
| snd_pcm_format_t format, | |
| snd_pcm_subformat_t subformat, | |
| snd_spcm_latency_t latency, | |
| snd_pcm_access_t access, | |
| snd_spcm_xrun_type_t xrun_type, | |
| snd_spcm_duplex_type_t duplex_type) | |
| { | |
| int err, i; | |
| snd_pcm_hw_params_t hw_params = {0}; | |
| snd_pcm_sw_params_t sw_params = {0}; | |
| unsigned int rrate; | |
| unsigned int xbuffer_time, buffer_time[2]; | |
| unsigned int period_time[2]; | |
| snd_pcm_t *pcms[2]; | |
| assert(playback_pcm); | |
| assert(capture_pcm); | |
| assert(rate >= 5000 && rate <= 768000); | |
| assert(channels >= 1 && channels <= 512); | |
| pcms[0] = playback_pcm; | |
| pcms[1] = capture_pcm; | |
| /* | |
| * hardware parameters | |
| */ | |
| err = set_buffer_time(latency, &xbuffer_time); | |
| if (err < 0) | |
| return err; | |
| for (i = 0; i < 2; i++) { | |
| buffer_time[i] = xbuffer_time; | |
| period_time[i] = i > 0 ? period_time[0] : 0; | |
| rrate = rate; | |
| err = set_hw_params(pcms[i], &hw_params, | |
| &rrate, channels, format, subformat, | |
| &buffer_time[i], &period_time[i], access); | |
| if (err < 0) | |
| return err; | |
| } | |
| if (buffer_time[0] == buffer_time[1] && | |
| period_time[0] == period_time[1]) | |
| goto __sw_params; | |
| if (duplex_type == SND_SPCM_DUPLEX_LIBERAL) | |
| goto __sw_params; | |
| /* FIXME: */ | |
| return -EINVAL; | |
| /* | |
| * software parameters | |
| */ | |
| __sw_params: | |
| for (i = 0; i < 2; i++) { | |
| err = set_sw_params(pcms[i], &sw_params, xrun_type); | |
| if (err < 0) | |
| return err; | |
| } | |
| return 0; | |
| } | |
| /** | |
| * \brief Get the set up of simple PCM | |
| * \param pcm PCM handle | |
| * \param rate Pointer to store the current sample rate | |
| * \param buffer_size Pointer to store the current buffer size | |
| * \param period_size Pointer to store the current period size | |
| * \return 0 if successful, or a negative error code | |
| * | |
| * \warning The simple PCM API may be broken in the current release. | |
| */ | |
| int snd_spcm_init_get_params(snd_pcm_t *pcm, | |
| unsigned int *rate, | |
| snd_pcm_uframes_t *buffer_size, | |
| snd_pcm_uframes_t *period_size) | |
| { | |
| assert(pcm); | |
| if (!pcm->setup) | |
| return -EBADFD; | |
| if (rate) | |
| *rate = pcm->rate; | |
| if (buffer_size) | |
| *buffer_size = pcm->buffer_size; | |
| if (period_size) | |
| *period_size = pcm->period_size; | |
| return 0; | |
| } |