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