xref: /third_party/alsa-lib/src/pcm/pcm_simple.c (revision d5ac70f0)
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