• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
set_buffer_time(snd_spcm_latency_t latency,unsigned int * buffer_time)28 static 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 
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)47 static 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 
set_sw_params(snd_pcm_t * pcm,snd_pcm_sw_params_t * sw_params,snd_spcm_xrun_type_t xrun_type)110 static 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  */
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)157 int 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  */
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)209 int 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  */
snd_spcm_init_get_params(snd_pcm_t * pcm,unsigned int * rate,snd_pcm_uframes_t * buffer_size,snd_pcm_uframes_t * period_size)284 int 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