comparison alsa_pcm_api.c @ 0:c55ea9478c80

Hello Gensokyo!
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 21 May 2013 10:29:21 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:c55ea9478c80
1 #include <stdint.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <fcntl.h>
5 #include <unistd.h>
6 #include <errno.h>
7 #include "alsa_pcm_api.h"
8 int snd_pcm_hw_params_any(int snd_fd, snd_pcm_hw_params_t *params);
9 int snd_pcm_hw_params_set_access(int snd_fd, snd_pcm_hw_params_t *params, snd_pcm_access_t access);
10 int snd_pcm_hw_params_set_format(int snd_fd, snd_pcm_hw_params_t *params, snd_pcm_format_t format);
11 int snd_pcm_hw_params_set_channels(int snd_fd, snd_pcm_hw_params_t *params, unsigned int val);
12 int snd_pcm_hw_params_set_rate(int snd_fd, snd_pcm_hw_params_t *params, unsigned int val, int dir);
13 int snd_pcm_hw_params_set_buffer_size(int snd_fd, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val);
14 #define MASK_SIZE (SND_MASK_MAX / 32)
15 #define MASK_OFS(i) ((i) >> 5)
16 #define MASK_BIT(i) (1U << ((i) & 31))
17
18 static inline
19 snd_interval_t *hw_param_interval(snd_pcm_hw_params_t *params, int var)
20 {
21 return &params->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL];
22 }
23
24 static inline
25 const snd_interval_t *hw_param_interval_c(const snd_pcm_hw_params_t *params, int var)
26 {
27 return (const snd_interval_t *)hw_param_interval((snd_pcm_hw_params_t*) params, var);
28 }
29
30 size_t snd_pcm_hw_params_sizeof(void)
31 {
32 return sizeof(snd_pcm_hw_params_t);
33 }
34
35 static void snd_interval_any(snd_interval_t *i)
36 {
37 i->min = 0;
38 i->openmin = 0;
39 i->max = -1;
40 i->openmax = 0;
41 i->integer = 0;
42 i->empty = 0;
43 }
44
45 static inline int snd_interval_empty(const snd_interval_t *i)
46 {
47 return i->empty;
48 }
49
50 static inline int snd_interval_checkempty(const snd_interval_t *i)
51 {
52 return (i->min > i->max ||
53 (i->min == i->max && (i->openmin || i->openmax)));
54 }
55
56 /* Return the minimum value for field PAR. */
57 int _snd_pcm_hw_param_get_min(const snd_pcm_hw_params_t *params,
58 int var, unsigned int *val, int *dir)
59 {
60 const snd_interval_t *i = hw_param_interval_c(params, var);
61 if (dir)
62 *dir = i->openmin;
63 if (val)
64 *val = i->min;
65 return 0;
66 }
67
68 /* Return the maximum value for field PAR. */
69 int _snd_pcm_hw_param_get_max(const snd_pcm_hw_params_t *params,
70 int var, unsigned int *val, int *dir)
71 {
72 const snd_interval_t *i = hw_param_interval_c(params, var);
73 if (dir)
74 *dir = - (int) i->openmax;
75 if (val)
76 *val = i->max;
77 return 0;
78 }
79
80 static void _snd_pcm_hw_param_any(snd_pcm_hw_params_t *params, int var)
81 {
82 snd_interval_any(hw_param_interval(params, var));
83 params->cmask |= 1 << var;
84 params->rmask |= 1 << var;
85 }
86
87 int snd_pcm_hw_params_any(int snd_fd, snd_pcm_hw_params_t *params)
88 {
89 unsigned int k;
90 memset(params, 0, sizeof(*params));
91 for (k = SNDRV_PCM_HW_PARAM_FIRST_MASK;
92 k <= SNDRV_PCM_HW_PARAM_LAST_MASK; k++) {
93 snd_mask_t *mask = &params->masks[k - SNDRV_PCM_HW_PARAM_FIRST_MASK];
94 memset(mask, 0xff, sizeof(*mask));
95 params->cmask |= 1 << k;
96 params->rmask |= 1 << k;
97 }
98 for (k = SNDRV_PCM_HW_PARAM_FIRST_INTERVAL;
99 k <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; k++)
100 _snd_pcm_hw_param_any(params, k);
101 params->rmask = ~0U;
102 params->cmask = 0;
103 params->info = ~0U;
104 return(ioctl(snd_fd, SNDRV_PCM_IOCTL_HW_REFINE, params));
105 }
106
107 int snd_pcm_hw_params_store(int snd_fd, snd_pcm_hw_params_t *params)
108 {
109 ioctl(snd_fd, SNDRV_PCM_IOCTL_HW_REFINE, params);
110 if (ioctl(snd_fd, SNDRV_PCM_IOCTL_HW_PARAMS, params) < 0)
111 return -1;
112 if (ioctl(snd_fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
113 return -1; //-errno;
114 return 0;
115 }
116
117 snd_pcm_sframes_t snd_pcm_writei(int snd_fd, const void *buffer, snd_pcm_uframes_t size)
118 {
119 struct snd_xferi xferi;
120 xferi.result = 0;
121 xferi.buf = (char*) buffer;
122 xferi.frames = size;
123 if (ioctl(snd_fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi) < 0)
124 return -1;
125 return xferi.result;
126 }
127
128 snd_pcm_sframes_t snd_pcm_writen(int snd_fd, void **bufs, snd_pcm_uframes_t size)
129 {
130 struct snd_xfern xfern;
131 xfern.result = 0;
132 xfern.bufs = bufs;
133 xfern.frames = size;
134 if (ioctl(snd_fd, SNDRV_PCM_IOCTL_WRITEN_FRAMES, &xfern) < 0)
135 return -1;
136 return xfern.result;
137 }
138
139 int snd_pcm_prepare(int snd_fd)
140 {
141 return ioctl(snd_fd, SNDRV_PCM_IOCTL_PREPARE);
142 }
143
144 int snd_pcm_recover(int snd_fd, int err, int silent)
145 {
146 if (err > 0)
147 err = -err;
148 switch (err) {
149 case -EINTR:
150 return 0;
151 case -EPIPE:
152 if (ioctl(snd_fd, SNDRV_PCM_IOCTL_PREPARE) < 0)
153 return -1; //-errno;
154 return 0;
155 }
156 return err;
157 }
158
159 int snd_pcm_status(int snd_fd, snd_pcm_status_t *status)
160 {
161 if (ioctl(snd_fd, SNDRV_PCM_IOCTL_STATUS, status) < 0)
162 return -1;
163 return 0;
164 }
165
166 static int hw_param_update_var(int snd_fd, snd_pcm_hw_params_t *params,
167 int var, int changed)
168 {
169 if (changed < 0)
170 return changed;
171 if (changed) {
172 params->cmask |= 1 << var;
173 params->rmask |= 1 << var;
174 }
175 if (params->rmask) {
176 changed = ioctl(snd_fd, SNDRV_PCM_IOCTL_HW_REFINE, params);
177 if (changed < 0)
178 return changed;
179 if (snd_interval_empty(hw_param_interval_c(params, var)))
180 return -ENOENT;
181 }
182 return 0;
183 }
184
185 static int snd_interval_refine(snd_interval_t *i, const snd_interval_t *v)
186 {
187 int changed = 0;
188 if (snd_interval_empty(i))
189 return -ENOENT;
190 if (i->min < v->min) {
191 i->min = v->min;
192 i->openmin = v->openmin;
193 changed = 1;
194 } else if (i->min == v->min && !i->openmin && v->openmin) {
195 i->openmin = 1;
196 changed = 1;
197 }
198 if (i->max > v->max) {
199 i->max = v->max;
200 i->openmax = v->openmax;
201 changed = 1;
202 } else if (i->max == v->max && !i->openmax && v->openmax) {
203 i->openmax = 1;
204 changed = 1;
205 }
206 if (!i->integer && v->integer) {
207 i->integer = 1;
208 changed = 1;
209 }
210 if (i->integer) {
211 if (i->openmin) {
212 i->min++;
213 i->openmin = 0;
214 }
215 if (i->openmax) {
216 i->max--;
217 i->openmax = 0;
218 }
219 } else if (!i->openmin && !i->openmax && i->min == i->max)
220 i->integer = 1;
221 if (snd_interval_checkempty(i)) {
222 i->empty = 1;
223 return -EINVAL;
224 }
225 return changed;
226 }
227
228 int _snd_pcm_hw_param_get(const snd_pcm_hw_params_t *params,
229 int var, unsigned int *val, int *dir)
230 {
231 const snd_interval_t *i = hw_param_interval_c(params, var);
232 if (snd_interval_empty(i) || !(i->min == i->max || ((i->min + 1 == i->max) && i->openmax)))
233 return -EINVAL;
234 if (dir)
235 *dir = i->openmin;
236 if (val)
237 *val = i->min;
238 return 0;
239 }
240
241 int _snd_pcm_hw_param_set(int snd_fd, snd_pcm_hw_params_t *params, int var,
242 unsigned int val, int dir)
243 {
244 int err = 0;
245 snd_pcm_hw_params_t save = *params;
246 snd_interval_t *i = hw_param_interval(params, var);
247 snd_interval_t t;
248 t.empty = 0;
249 t.min = t.max = var;
250 t.openmin = t.openmax = 0;
251 t.integer = 1;
252 err = snd_interval_refine(i, &t);
253 err = hw_param_update_var(snd_fd, params, var, err);
254 if (err)
255 *params = save;
256 return err;
257 }
258
259 static inline int _snd_pcm_hw_param_get64(const snd_pcm_hw_params_t *params, int type, unsigned long *val, int *dir)
260 {
261 unsigned int _val;
262 int err = _snd_pcm_hw_param_get(params, type, &_val, dir);
263 *val = _val;
264 return err;
265 }
266
267 #if 0
268 int snd_pcm_hw_params_get_channels(const snd_pcm_hw_params_t *params, unsigned int *val)
269 {
270 return _snd_pcm_hw_param_get(params, SNDRV_PCM_HW_PARAM_CHANNELS, val, NULL);
271 }
272
273 int snd_pcm_hw_params_set_channels(int snd_fd, snd_pcm_hw_params_t *params, unsigned int val)
274 {
275 return _snd_pcm_hw_param_set(snd_fd, params, SNDRV_PCM_HW_PARAM_CHANNELS, val, 0);
276 }
277
278 int snd_pcm_hw_params_get_rate(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir)
279 {
280 return _snd_pcm_hw_param_get(params, SNDRV_PCM_HW_PARAM_RATE, val, dir);
281 }
282
283 int snd_pcm_hw_params_set_rate(int snd_fd, snd_pcm_hw_params_t *params, unsigned int val, int dir)
284 {
285 return _snd_pcm_hw_param_set(snd_fd, params, SNDRV_PCM_HW_PARAM_RATE, val, dir);
286 }
287
288 int snd_pcm_hw_params_get_period_time(const snd_pcm_hw_params_t *params,
289 unsigned int *val, int *dir)
290 {
291 return _snd_pcm_hw_param_get(params, SNDRV_PCM_HW_PARAM_PERIOD_TIME, val, dir);
292 }
293
294 int snd_pcm_hw_params_get_period_size(const snd_pcm_hw_params_t *params,
295 snd_pcm_uframes_t *val, int *dir)
296 {
297 return _snd_pcm_hw_param_get64(params, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, val, dir);
298 }
299
300 int snd_pcm_hw_params_get_buffer_size(const snd_pcm_hw_params_t *params,
301 snd_pcm_uframes_t *val)
302 {
303 return _snd_pcm_hw_param_get64(params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, val, NULL);
304 }
305
306 int snd_pcm_hw_params_set_buffer_size(int snd_fd, snd_pcm_hw_params_t *params,
307 snd_pcm_uframes_t val)
308 {
309 return _snd_pcm_hw_param_set(snd_fd, params, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, val, 0);
310 }
311 #endif
312
313 static inline unsigned int popcount32(uint32_t mask)
314 {
315 register unsigned int y;
316 y = (mask >> 1) &033333333333;
317 y = mask - y - ((y >>1) & 033333333333);
318 return (((y + (y >> 3)) & 030707070707) % 077);
319 }
320
321 int snd_pcm_hw_params_set_access(int pcm_fd, snd_pcm_hw_params_t *params,
322 snd_pcm_access_t access)
323 {
324 snd_pcm_hw_params_t save = *params;
325 snd_mask_t *param = (snd_mask_t*)&params->masks[SNDRV_PCM_HW_PARAM_ACCESS - SNDRV_PCM_HW_PARAM_FIRST_MASK];
326 uint32_t i, v = 0;
327 int err = 0, changed;
328 for (i = 0; i < MASK_SIZE; i++) {
329 v += popcount32(param->bits[i]);
330 }
331 changed = (v != 1);
332 v = param->bits[MASK_OFS(access)] & MASK_BIT(access);
333 memset(param, 0, sizeof(*param));
334 param->bits[MASK_OFS(access)] = v;
335 if (changed) {
336 params->cmask |= 1 << SNDRV_PCM_HW_PARAM_ACCESS;
337 params->rmask |= 1 << SNDRV_PCM_HW_PARAM_ACCESS;
338 }
339 if (params->rmask) {
340 err = ioctl(pcm_fd, SNDRV_PCM_IOCTL_HW_REFINE, params);
341 if (err)
342 *params = save;
343 }
344 return err;
345 }
346
347 int snd_pcm_hw_params_set_format(int pcm_fd, snd_pcm_hw_params_t *params,
348 snd_pcm_format_t format)
349 {
350 snd_pcm_hw_params_t save = *params;
351 snd_mask_t *param = (snd_mask_t*)&params->masks[SNDRV_PCM_HW_PARAM_FORMAT - SNDRV_PCM_HW_PARAM_FIRST_MASK];
352 uint32_t i, v = 0;
353 int err = 0, changed;
354 for (i = 0; i < MASK_SIZE; i++) {
355 v += popcount32(param->bits[i]);
356 }
357 changed = (v != 1);
358 v = param->bits[MASK_OFS(format)] & MASK_BIT(format);
359 memset(param, 0, sizeof(*param));
360 param->bits[MASK_OFS(format)] = v;
361 if (changed) {
362 params->cmask |= 1 << SNDRV_PCM_HW_PARAM_ACCESS;
363 params->rmask |= 1 << SNDRV_PCM_HW_PARAM_ACCESS;
364 }
365 if (params->rmask) {
366 err = ioctl(pcm_fd, SNDRV_PCM_IOCTL_HW_REFINE, params);
367 if (err)
368 *params = save;
369 }
370 return err;
371 }
372
373 int alsa_audio_close(int snd_fd)
374 {
375 ioctl(snd_fd, SNDRV_PCM_IOCTL_HW_FREE);
376 return close(snd_fd);
377 }
378
379 int alsa_audio_open(uint32_t *rate, uint8_t channels, uint32_t *_ver, int mode)
380 {
381 uint32_t actual_channels;
382 snd_pcm_hw_params_t *hwparams;
383 *&hwparams = (snd_pcm_hw_params_t *) alloca(snd_pcm_hw_params_sizeof());
384 int ver, snd_fd = open("/dev/snd/pcmC0D0p", O_WRONLY|mode);
385 if (snd_fd < 0)
386 return -1;
387
388 if (ioctl(snd_fd, SNDRV_PCM_IOCTL_PVERSION, &ver) < 0) {
389 if (snd_fd >= 0)
390 close(snd_fd);
391 return -1;
392 }
393 if(_ver) *_ver = ver;
394
395 // Initialize hwparams structure.
396 snd_pcm_hw_params_any(snd_fd, hwparams);
397
398 // this *must* be set, or else the writei (actually an ioctl(), wtf?) below will fail with -EBADF
399 snd_pcm_hw_params_set_access(snd_fd, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
400 snd_pcm_hw_params_set_format(snd_fd, hwparams, SND_PCM_FORMAT_S16_LE);
401
402 _snd_pcm_hw_param_set(snd_fd, hwparams, SNDRV_PCM_HW_PARAM_CHANNELS, channels, 0);
403 _snd_pcm_hw_param_get(hwparams, SNDRV_PCM_HW_PARAM_CHANNELS, &actual_channels, NULL);
404 if ((channels == 1) && (actual_channels == 2)) {
405 // This is an offensively dumb hack that works around the fact that the ALSA devs erroneously believe that HDAudio chipsets cannot playback mono.
406 // On any other chipset, it's a terrible idea. In general, it's awful. I'm just lazy. That, and I don't use ALSA myself. :P
407 *rate >>= 1;
408 }
409 _snd_pcm_hw_param_set(snd_fd, hwparams, SNDRV_PCM_HW_PARAM_RATE, *rate, 0);
410 snd_pcm_hw_params_store(snd_fd, hwparams);
411 _snd_pcm_hw_param_get(hwparams, SNDRV_PCM_HW_PARAM_RATE, rate, NULL);
412 if ((channels == 1) && (actual_channels == 2)) {
413 *rate <<= 1;
414 }
415 return snd_fd;
416 }
417