blob: 3ad0521aa6eed5b4749f7443913d772cabca7c5a [file] [log] [blame]
Paul Kocialkowski15796922017-08-21 18:27:19 +03001/*
2 * Copyright © 2017 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 * Authors:
24 * Paul Kocialkowski <paul.kocialkowski@linux.intel.com>
25 */
26
27#include "config.h"
28
29#include <alsa/asoundlib.h>
30
Daniel Vetter8c1fcc62017-09-08 11:23:15 +020031#include "igt_alsa.h"
32#include "igt_core.h"
Paul Kocialkowski15796922017-08-21 18:27:19 +030033
34#define HANDLES_MAX 8
35
36/**
37 * SECTION:igt_alsa
38 * @short_description: Library with ALSA helpers
39 * @title: ALSA
40 * @include: igt_alsa.h
41 *
42 * This library contains helpers for ALSA playback and capture.
43 */
44
45struct alsa {
46 snd_pcm_t *output_handles[HANDLES_MAX];
47 int output_handles_count;
48 int output_sampling_rate;
49 int output_channels;
50
51 int (*output_callback)(void *data, short *buffer, int samples);
52 void *output_callback_data;
53 int output_samples_trigger;
54
55 snd_pcm_t *input_handle;
56 int input_sampling_rate;
57 int input_channels;
58
59 int (*input_callback)(void *data, short *buffer, int samples);
60 void *input_callback_data;
61 int input_samples_trigger;
62};
63
64static void alsa_error_handler(const char *file, int line, const char *function,
65 int err, const char *fmt, ...)
66{
67 if (err)
68 igt_debug("[ALSA] %s: %s\n", function, snd_strerror(err));
69}
70
71/**
72 * alsa_init:
73 * Allocate and initialize an alsa structure and configure the error handler.
74 *
75 * Returns: A newly-allocated alsa structure
76 */
77struct alsa *alsa_init(void)
78{
79 struct alsa *alsa;
80
81 alsa = malloc(sizeof(struct alsa));
82 memset(alsa, 0, sizeof(struct alsa));
83
84 /* Redirect errors to igt_debug instead of stderr. */
85 snd_lib_error_set_handler(alsa_error_handler);
86
87 return alsa;
88}
89
90static char *alsa_resolve_indentifier(const char *device_name, int skip)
91{
92 snd_ctl_card_info_t *card_info;
93 snd_pcm_info_t *pcm_info;
94 snd_ctl_t *handle = NULL;
95 const char *pcm_name;
96 char *identifier = NULL;
97 char name[32];
98 int card = -1;
99 int dev;
100 int ret;
101
102 snd_ctl_card_info_alloca(&card_info);
103 snd_pcm_info_alloca(&pcm_info);
104
105 /* First try to open the device as-is. */
106 if (!skip) {
107 ret = snd_ctl_open(&handle, device_name, 0);
108 if (!ret) {
109 identifier = strdup(device_name);
110 goto resolved;
111 }
112 }
113
114 do {
115 ret = snd_card_next(&card);
116 if (ret < 0 || card < 0)
117 break;
118
119 snprintf(name, sizeof(name), "hw:%d", card);
120
121 ret = snd_ctl_open(&handle, name, 0);
122 if (ret < 0)
123 continue;
124
125 ret = snd_ctl_card_info(handle, card_info);
126 if (ret < 0) {
127 snd_ctl_close(handle);
128 handle = NULL;
129 continue;
130 }
131
132 dev = -1;
133
134 do {
135 ret = snd_ctl_pcm_next_device(handle, &dev);
136 if (ret < 0 || dev < 0)
137 break;
138
139 snd_pcm_info_set_device(pcm_info, dev);
140 snd_pcm_info_set_subdevice(pcm_info, 0);
141
142 ret = snd_ctl_pcm_info(handle, pcm_info);
143 if (ret < 0)
144 continue;
145
146 pcm_name = snd_pcm_info_get_name(pcm_info);
147 if (!pcm_name)
148 continue;
149
150 ret = strncmp(device_name, pcm_name,
151 strlen(device_name));
152
153 if (ret == 0) {
154 if (skip > 0) {
155 skip--;
156 continue;
157 }
158
159 snprintf(name, sizeof(name), "hw:%d,%d", card,
160 dev);
161
162 identifier = strdup(name);
163 goto resolved;
164 }
165 } while (dev >= 0);
166
167 snd_ctl_close(handle);
168 handle = NULL;
169 } while (card >= 0);
170
171resolved:
172 if (handle)
173 snd_ctl_close(handle);
174
175 return identifier;
176}
177
178/**
179 * alsa_open_output:
180 * @alsa: The target alsa structure
181 * @device_name: The name prefix of the output device(s) to open
182 *
183 * Open ALSA output devices whose name prefixes match the provided name prefix.
184 *
185 * Returns: An integer equal to zero for success and negative for failure
186 */
187int alsa_open_output(struct alsa *alsa, const char *device_name)
188{
189 snd_pcm_t *handle;
190 char *identifier;
191 int skip;
192 int index;
193 int ret;
194
195 skip = alsa->output_handles_count;
196 index = alsa->output_handles_count;
197
198 while (index < HANDLES_MAX) {
199 identifier = alsa_resolve_indentifier(device_name, skip++);
200 if (!identifier)
201 break;
202
203 ret = snd_pcm_open(&handle, identifier, SND_PCM_STREAM_PLAYBACK,
204 SND_PCM_NONBLOCK);
205 if (ret < 0) {
206 free(identifier);
207 continue;
208 }
209
210 igt_debug("Opened output %s\n", identifier);
211
212 alsa->output_handles[index++] = handle;
213 free(identifier);
214 }
215
216 if (index == 0)
217 return -1;
218
219 alsa->output_handles_count = index;
220
221 return 0;
222}
223
224/**
225 * alsa_open_input:
226 * @alsa: The target alsa structure
227 * @device_name: The name of the input device to open
228 *
229 * Open the ALSA input device whose name matches the provided name prefix.
230 *
231 * Returns: An integer equal to zero for success and negative for failure
232 */
233int alsa_open_input(struct alsa *alsa, const char *device_name)
234{
235 snd_pcm_t *handle;
236 char *identifier;
237 int ret;
238
239 identifier = alsa_resolve_indentifier(device_name, 0);
240
241 ret = snd_pcm_open(&handle, device_name, SND_PCM_STREAM_CAPTURE,
242 SND_PCM_NONBLOCK);
243 if (ret < 0)
244 goto complete;
245
246 igt_debug("Opened input %s\n", identifier);
247
248 alsa->input_handle = handle;
249
250 ret = 0;
251
252complete:
253 free(identifier);
254
255 return ret;
256}
257
258/**
259 * alsa_close_output:
260 * @alsa: The target alsa structure
261 *
262 * Close all the open ALSA outputs.
263 */
264void alsa_close_output(struct alsa *alsa)
265{
266 snd_pcm_t *handle;
267 int i;
268
269 for (i = 0; i < alsa->output_handles_count; i++) {
270 handle = alsa->output_handles[i];
271 if (!handle)
272 continue;
273
274 snd_pcm_close(handle);
275 alsa->output_handles[i] = NULL;
276 }
277
278 alsa->output_handles_count = 0;
279
280 alsa->output_callback = NULL;
281}
282
283/**
284 * alsa_close_output:
285 * @alsa: The target alsa structure
286 *
287 * Close the open ALSA input.
288 */
289void alsa_close_input(struct alsa *alsa)
290{
291 snd_pcm_t *handle = alsa->input_handle;
292 if (!handle)
293 return;
294
295 snd_pcm_close(handle);
296 alsa->input_handle = NULL;
297
298 alsa->input_callback = NULL;
299}
300
301static bool alsa_test_configuration(snd_pcm_t *handle, int channels,
302 int sampling_rate)
303{
304 snd_pcm_hw_params_t *params;
305 int ret;
306
307 snd_pcm_hw_params_alloca(&params);
308
309 ret = snd_pcm_hw_params_any(handle, params);
310 if (ret < 0)
311 return false;
312
313 ret = snd_pcm_hw_params_test_rate(handle, params, sampling_rate, 0);
314 if (ret < 0)
315 return false;
316
317 ret = snd_pcm_hw_params_test_channels(handle, params, channels);
318 if (ret < 0)
319 return false;
320
321 return true;
322}
323
324/**
325 * alsa_test_output_configuration:
326 * @alsa: The target alsa structure
327 * @channels: The number of channels to test
328 * @sampling_rate: The sampling rate to test
329 *
330 * Test the output configuration specified by @channels and @sampling_rate
331 * for the output devices.
332 *
333 * Returns: A boolean indicating whether the test succeeded
334 */
335bool alsa_test_output_configuration(struct alsa *alsa, int channels,
336 int sampling_rate)
337{
338 snd_pcm_t *handle;
339 bool ret;
340 int i;
341
342 for (i = 0; i < alsa->output_handles_count; i++) {
343 handle = alsa->output_handles[i];
344
345 ret = alsa_test_configuration(handle, channels, sampling_rate);
346 if (!ret)
347 return false;
348 }
349
350 return true;
351}
352
353/**
354 * alsa_test_input_configuration:
355 * @alsa: The target alsa structure
356 * @channels: The number of channels to test
357 * @sampling_rate: The sampling rate to test
358 *
359 * Test the input configuration specified by @channels and @sampling_rate
360 * for the input device.
361 *
362 * Returns: A boolean indicating whether the test succeeded
363 */
364bool alsa_test_input_configuration(struct alsa *alsa, int channels,
365 int sampling_rate)
366{
367 return alsa_test_configuration(alsa->input_handle, channels,
368 sampling_rate);
369}
370
371/**
372 * alsa_configure_output:
373 * @alsa: The target alsa structure
374 * @channels: The number of channels to test
375 * @sampling_rate: The sampling rate to test
376 *
377 * Configure the output devices with the configuration specified by @channels
378 * and @sampling_rate.
379 */
380void alsa_configure_output(struct alsa *alsa, int channels,
381 int sampling_rate)
382{
383 snd_pcm_t *handle;
384 int ret;
385 int i;
386
387 for (i = 0; i < alsa->output_handles_count; i++) {
388 handle = alsa->output_handles[i];
389
390 ret = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE,
391 SND_PCM_ACCESS_RW_INTERLEAVED,
392 channels, sampling_rate, 0, 0);
393 igt_assert(ret >= 0);
394 }
395
396 alsa->output_channels = channels;
397 alsa->output_sampling_rate = sampling_rate;
398}
399
400/**
401 * alsa_configure_input:
402 * @alsa: The target alsa structure
403 * @channels: The number of channels to test
404 * @sampling_rate: The sampling rate to test
405 *
406 * Configure the input device with the configuration specified by @channels
407 * and @sampling_rate.
408 */
409void alsa_configure_input(struct alsa *alsa, int channels,
410 int sampling_rate)
411{
412 snd_pcm_t *handle;
413 int ret;
414
415 handle = alsa->input_handle;
416
417 ret = snd_pcm_set_params(handle, SND_PCM_FORMAT_S16_LE,
418 SND_PCM_ACCESS_RW_INTERLEAVED, channels,
419 sampling_rate, 0, 0);
420 igt_assert(ret >= 0);
421
422 alsa->input_channels = channels;
423 alsa->input_sampling_rate = sampling_rate;
424
425}
426
427/**
428 * alsa_register_output_callback:
429 * @alsa: The target alsa structure
430 * @callback: The callback function to call to fill output data
431 * @callback_data: The data pointer to pass to the callback function
432 * @samples_trigger: The required number of samples to trigger the callback
433 *
434 * Register a callback function to be called to fill output data during a run.
435 * The callback is called when @samples_trigger samples are required.
436 *
437 * The callback should return an integer equal to zero for success and negative
438 * for failure.
439 */
440void alsa_register_output_callback(struct alsa *alsa,
441 int (*callback)(void *data, short *buffer, int samples),
442 void *callback_data, int samples_trigger)
443{
444 alsa->output_callback = callback;
445 alsa->output_callback_data = callback_data;
446 alsa->output_samples_trigger = samples_trigger;
447}
448
449/**
450 * alsa_register_input_callback:
451 * @alsa: The target alsa structure
452 * @callback: The callback function to call when input data is available
453 * @callback_data: The data pointer to pass to the callback function
454 * @samples_trigger: The required number of samples to trigger the callback
455 *
456 * Register a callback function to be called when input data is available during
457 * a run. The callback is called when @samples_trigger samples are available.
458 *
459 * The callback should return an integer equal to zero for success, negative for
460 * failure and positive to indicate that the run should stop.
461 */
462void alsa_register_input_callback(struct alsa *alsa,
463 int (*callback)(void *data, short *buffer, int samples),
464 void *callback_data, int samples_trigger)
465{
466 alsa->input_callback = callback;
467 alsa->input_callback_data = callback_data;
468 alsa->input_samples_trigger = samples_trigger;
469}
470
471/**
472 * alsa_run:
473 * @alsa: The target alsa structure
474 * @duration_ms: The maximum duration of the run in milliseconds
475 *
476 * Run ALSA playback and capture on the input and output devices for at
477 * most @duration_ms milliseconds, calling the registered callbacks when needed.
478 *
479 * Returns: An integer equal to zero for success, positive for a stop caused
480 * by the input callback and negative for failure
481 */
482int alsa_run(struct alsa *alsa, int duration_ms)
483{
484 snd_pcm_t *handle;
485 short *output_buffer = NULL;
486 short *input_buffer = NULL;
487 int output_limit;
488 int output_total = 0;
489 int output_counts[alsa->output_handles_count];
490 bool output_ready = false;
491 int output_channels;
492 int output_trigger;
493 int input_limit;
494 int input_total = 0;
495 int input_count = 0;
496 int input_channels;
497 int input_trigger;
498 bool reached;
499 int index;
500 int count;
501 int avail;
502 int i;
503 int ret;
504
505 output_limit = alsa->output_sampling_rate * duration_ms / 1000;
506 output_channels = alsa->output_channels;
507 output_trigger = alsa->output_samples_trigger;
508 output_buffer = malloc(sizeof(short) * output_channels *
509 output_trigger);
510
511 if (alsa->input_callback) {
512 input_limit = alsa->input_sampling_rate * duration_ms / 1000;
513 input_trigger = alsa->input_samples_trigger;
514 input_channels = alsa->input_channels;
515 input_buffer = malloc(sizeof(short) * input_channels *
516 input_trigger);
517 }
518
519 do {
520 reached = true;
521
522 if (output_total < output_limit) {
523 reached = false;
524
525 if (!output_ready) {
526 output_ready = true;
527
528 for (i = 0; i < alsa->output_handles_count; i++)
529 output_counts[i] = 0;
530
531 ret = alsa->output_callback(alsa->output_callback_data,
532 output_buffer,
533 output_trigger);
534 if (ret < 0)
535 goto complete;
536 }
537
538 for (i = 0; i < alsa->output_handles_count; i++) {
539 handle = alsa->output_handles[i];
540
541 ret = snd_pcm_avail(handle);
542 if (output_counts[i] < output_trigger &&
543 ret > 0) {
544 index = output_counts[i] *
545 output_channels;
546 count = output_trigger -
547 output_counts[i];
548 avail = snd_pcm_avail(handle);
549
550 count = avail < count ? avail : count;
551
552 ret = snd_pcm_writei(handle,
553 &output_buffer[index],
554 count);
555 if (ret < 0) {
556 ret = snd_pcm_recover(handle,
557 ret, 0);
558 if (ret < 0)
559 goto complete;
560 }
561
562 output_counts[i] += ret;
563 } else if (output_counts[i] < output_trigger &&
564 ret < 0) {
565 ret = snd_pcm_recover(handle, ret, 0);
566 if (ret < 0)
567 goto complete;
568 }
569 }
570
571 output_ready = false;
572
573 for (i = 0; i < alsa->output_handles_count; i++)
574 if (output_counts[i] < output_trigger)
575 output_ready = true;
576
577 if (!output_ready)
578 output_total += output_trigger;
579
580 }
581
582 if (alsa->input_callback && input_total < input_limit) {
583 reached = false;
584
585 if (input_count == input_trigger) {
586 input_count = 0;
587
588 ret = alsa->input_callback(alsa->input_callback_data,
589 input_buffer,
590 input_trigger);
591 if (ret != 0)
592 goto complete;
593 }
594
595 handle = alsa->input_handle;
596
597 ret = snd_pcm_avail(handle);
598 if (input_count < input_trigger &&
599 (ret > 0 || input_total == 0)) {
600 index = input_count * input_channels;
601 count = input_trigger - input_count;
602 avail = snd_pcm_avail(handle);
603
604 count = avail > 0 && avail < count ? avail :
605 count;
606
607 ret = snd_pcm_readi(handle,
608 &input_buffer[index],
609 count);
610 if (ret == -EAGAIN) {
611 ret = 0;
612 } else if (ret < 0) {
613 ret = snd_pcm_recover(handle, ret, 0);
614 if (ret < 0)
615 goto complete;
616 }
617
618 input_count += ret;
619 input_total += ret;
620 } else if (input_count < input_trigger && ret < 0) {
621 ret = snd_pcm_recover(handle, ret, 0);
622 if (ret < 0)
623 goto complete;
624 }
625 }
626 } while (!reached);
627
628 ret = 0;
629
630complete:
631 if (output_buffer)
632 free(output_buffer);
633
634 if (input_buffer)
635 free(input_buffer);
636
637 return ret;
638}