1/** 2 * \file pcm/pcm_simple.c 3 * \ingroup PCM_Simple 4 * \brief PCM Simple Interface 5 * \author Jaroslav Kysela <perex@perex.cz> 6 * \date 2004 7 */ 8/* 9 * 10 * This library is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU Lesser General Public License as 12 * published by the Free Software Foundation; either version 2.1 of 13 * the License, or (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with this library; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 * 24 */ 25 26#include "pcm_local.h" 27 28static int set_buffer_time(snd_spcm_latency_t latency, 29 unsigned int *buffer_time) 30{ 31 switch (latency) { 32 case SND_SPCM_LATENCY_STANDARD: 33 *buffer_time = 350000; 34 break; 35 case SND_SPCM_LATENCY_MEDIUM: 36 *buffer_time = 25000; 37 break; 38 case SND_SPCM_LATENCY_REALTIME: 39 *buffer_time = 2500; 40 break; 41 default: 42 return -EINVAL; 43 } 44 return 0; 45} 46 47static int set_hw_params(snd_pcm_t *pcm, 48 snd_pcm_hw_params_t *hw_params, 49 unsigned int *rate, 50 unsigned int channels, 51 snd_pcm_format_t format, 52 snd_pcm_subformat_t subformat, 53 unsigned int *buffer_time, 54 unsigned int *period_time, 55 snd_pcm_access_t access) 56{ 57 int err; 58 59 /* 60 * hardware parameters 61 */ 62 err = snd_pcm_hw_params_any(pcm, hw_params); 63 if (err < 0) 64 return err; 65 err = snd_pcm_hw_params_set_access(pcm, hw_params, access); 66 if (err < 0) 67 return err; 68 err = snd_pcm_hw_params_set_format(pcm, hw_params, format); 69 if (err < 0) 70 return err; 71 if (subformat != SND_PCM_SUBFORMAT_STD) { 72 err = snd_pcm_hw_params_set_subformat(pcm, hw_params, subformat); 73 if (err < 0) 74 return err; 75 } 76 err = snd_pcm_hw_params_set_channels(pcm, hw_params, channels); 77 if (err < 0) 78 return err; 79 err = INTERNAL(snd_pcm_hw_params_set_rate_near)(pcm, hw_params, rate, 0); 80 if (err < 0) 81 return err; 82 err = INTERNAL(snd_pcm_hw_params_set_buffer_time_near)(pcm, hw_params, buffer_time, NULL); 83 if (err < 0) 84 return err; 85 if (period_time == NULL || *period_time == 0) { 86 unsigned int periods = 3; 87 err = INTERNAL(snd_pcm_hw_params_set_periods_near)(pcm, hw_params, &periods, NULL); 88 if (err < 0) 89 return err; 90 if (periods == 1) 91 return -EINVAL; 92 if (period_time) { 93 err = INTERNAL(snd_pcm_hw_params_get_period_time)(hw_params, period_time, NULL); 94 if (err < 0) 95 return err; 96 } 97 } else { 98 err = snd_pcm_hw_params_set_period_time(pcm, hw_params, *period_time, 0); 99 if (err < 0) 100 return err; 101 if (*buffer_time == *period_time) 102 return -EINVAL; 103 } 104 err = snd_pcm_hw_params(pcm, hw_params); 105 if (err < 0) 106 return err; 107 return 0; 108} 109 110static int set_sw_params(snd_pcm_t *pcm, 111 snd_pcm_sw_params_t *sw_params, 112 snd_spcm_xrun_type_t xrun_type) 113{ 114 int err; 115 116 err = snd_pcm_sw_params_current(pcm, sw_params); 117 if (err < 0) 118 return err; 119 err = snd_pcm_sw_params_set_start_threshold(pcm, sw_params, (pcm->buffer_size / pcm->period_size) * pcm->period_size); 120 if (err < 0) 121 return err; 122 err = snd_pcm_sw_params_set_avail_min(pcm, sw_params, pcm->period_size); 123 if (err < 0) 124 return err; 125 switch (xrun_type) { 126 case SND_SPCM_XRUN_STOP: 127 err = snd_pcm_sw_params_set_stop_threshold(pcm, sw_params, pcm->buffer_size); 128 break; 129 case SND_SPCM_XRUN_IGNORE: 130 err = snd_pcm_sw_params_set_stop_threshold(pcm, sw_params, pcm->boundary); 131 break; 132 default: 133 return -EINVAL; 134 } 135 if (err < 0) 136 return err; 137 err = snd_pcm_sw_params(pcm, sw_params); 138 if (err < 0) 139 return err; 140 return 0; 141} 142 143/** 144 * \brief Set up a simple PCM 145 * \param pcm PCM handle 146 * \param rate Sample rate 147 * \param channels Number of channels 148 * \param format PCM format 149 * \param subformat PCM subformat 150 * \param latency Latency type 151 * \param access PCM acceess type 152 * \param xrun_type XRUN type 153 * \return 0 if successful, or a negative error code 154 * 155 * \warning The simple PCM API may be broken in the current release. 156 */ 157int snd_spcm_init(snd_pcm_t *pcm, 158 unsigned int rate, 159 unsigned int channels, 160 snd_pcm_format_t format, 161 snd_pcm_subformat_t subformat, 162 snd_spcm_latency_t latency, 163 snd_pcm_access_t access, 164 snd_spcm_xrun_type_t xrun_type) 165{ 166 int err; 167 snd_pcm_hw_params_t hw_params = {0}; 168 snd_pcm_sw_params_t sw_params = {0}; 169 unsigned int rrate; 170 unsigned int buffer_time; 171 172 assert(pcm); 173 assert(rate >= 5000 && rate <= 786000); 174 assert(channels >= 1 && channels <= 512); 175 176 rrate = rate; 177 err = set_buffer_time(latency, &buffer_time); 178 if (err < 0) 179 return err; 180 err = set_hw_params(pcm, &hw_params, 181 &rrate, channels, format, subformat, 182 &buffer_time, NULL, access); 183 if (err < 0) 184 return err; 185 186 err = set_sw_params(pcm, &sw_params, xrun_type); 187 if (err < 0) 188 return err; 189 190 return 0; 191} 192 193/** 194 * \brief Initialize simple PCMs in the duplex mode 195 * \param playback_pcm PCM handle for playback 196 * \param capture_pcm PCM handle for capture 197 * \param rate Sample rate 198 * \param channels Number of channels 199 * \param format PCM format 200 * \param subformat PCM subformat 201 * \param latency Latency type 202 * \param access PCM acceess type 203 * \param xrun_type XRUN type 204 * \param duplex_type Duplex mode 205 * \return 0 if successful, or a negative error code 206 * 207 * \warning The simple PCM API may be broken in the current release. 208 */ 209int snd_spcm_init_duplex(snd_pcm_t *playback_pcm, 210 snd_pcm_t *capture_pcm, 211 unsigned int rate, 212 unsigned int channels, 213 snd_pcm_format_t format, 214 snd_pcm_subformat_t subformat, 215 snd_spcm_latency_t latency, 216 snd_pcm_access_t access, 217 snd_spcm_xrun_type_t xrun_type, 218 snd_spcm_duplex_type_t duplex_type) 219{ 220 int err, i; 221 snd_pcm_hw_params_t hw_params = {0}; 222 snd_pcm_sw_params_t sw_params = {0}; 223 unsigned int rrate; 224 unsigned int xbuffer_time, buffer_time[2]; 225 unsigned int period_time[2]; 226 snd_pcm_t *pcms[2]; 227 228 assert(playback_pcm); 229 assert(capture_pcm); 230 assert(rate >= 5000 && rate <= 768000); 231 assert(channels >= 1 && channels <= 512); 232 233 pcms[0] = playback_pcm; 234 pcms[1] = capture_pcm; 235 236 /* 237 * hardware parameters 238 */ 239 err = set_buffer_time(latency, &xbuffer_time); 240 if (err < 0) 241 return err; 242 243 for (i = 0; i < 2; i++) { 244 buffer_time[i] = xbuffer_time; 245 period_time[i] = i > 0 ? period_time[0] : 0; 246 rrate = rate; 247 err = set_hw_params(pcms[i], &hw_params, 248 &rrate, channels, format, subformat, 249 &buffer_time[i], &period_time[i], access); 250 if (err < 0) 251 return err; 252 } 253 if (buffer_time[0] == buffer_time[1] && 254 period_time[0] == period_time[1]) 255 goto __sw_params; 256 if (duplex_type == SND_SPCM_DUPLEX_LIBERAL) 257 goto __sw_params; 258 /* FIXME: */ 259 return -EINVAL; 260 261 /* 262 * software parameters 263 */ 264 __sw_params: 265 for (i = 0; i < 2; i++) { 266 err = set_sw_params(pcms[i], &sw_params, xrun_type); 267 if (err < 0) 268 return err; 269 } 270 271 return 0; 272} 273 274/** 275 * \brief Get the set up of simple PCM 276 * \param pcm PCM handle 277 * \param rate Pointer to store the current sample rate 278 * \param buffer_size Pointer to store the current buffer size 279 * \param period_size Pointer to store the current period size 280 * \return 0 if successful, or a negative error code 281 * 282 * \warning The simple PCM API may be broken in the current release. 283 */ 284int snd_spcm_init_get_params(snd_pcm_t *pcm, 285 unsigned int *rate, 286 snd_pcm_uframes_t *buffer_size, 287 snd_pcm_uframes_t *period_size) 288{ 289 assert(pcm); 290 if (!pcm->setup) 291 return -EBADFD; 292 if (rate) 293 *rate = pcm->rate; 294 if (buffer_size) 295 *buffer_size = pcm->buffer_size; 296 if (period_size) 297 *period_size = pcm->period_size; 298 return 0; 299} 300