• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013-2015 Intel Corporation
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  */
15 
16 #include <stdio.h>
17 #include <string.h>
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <pthread.h>
21 #include <errno.h>
22 
23 #include <alsa/asoundlib.h>
24 
25 #include "aconfig.h"
26 #include "gettext.h"
27 
28 #include "common.h"
29 #include "alsa.h"
30 #include "latencytest.h"
31 
32 struct pcm_container {
33 	snd_pcm_t *handle;
34 	snd_pcm_uframes_t period_size;
35 	snd_pcm_uframes_t buffer_size;
36 	snd_pcm_format_t format;
37 	unsigned short channels;
38 	size_t period_bytes;
39 	size_t sample_bits;
40 	size_t frame_bits;
41 	char *buffer;
42 };
43 
44 struct format_map_table {
45 	enum _bat_pcm_format format_bat;
46 	snd_pcm_format_t format_alsa;
47 };
48 
49 static struct format_map_table map_tables[] = {
50 	{ BAT_PCM_FORMAT_UNKNOWN, SND_PCM_FORMAT_UNKNOWN },
51 	{ BAT_PCM_FORMAT_U8, SND_PCM_FORMAT_U8 },
52 	{ BAT_PCM_FORMAT_S16_LE, SND_PCM_FORMAT_S16_LE },
53 	{ BAT_PCM_FORMAT_S24_3LE, SND_PCM_FORMAT_S24_3LE },
54 	{ BAT_PCM_FORMAT_S32_LE, SND_PCM_FORMAT_S32_LE },
55 	{ BAT_PCM_FORMAT_MAX, },
56 };
57 
format_convert(struct bat * bat,snd_pcm_format_t * fmt)58 static int format_convert(struct bat *bat, snd_pcm_format_t *fmt)
59 {
60 	struct format_map_table *t = map_tables;
61 
62 	for (; t->format_bat != BAT_PCM_FORMAT_MAX; t++) {
63 		if (t->format_bat == bat->format) {
64 			*fmt = t->format_alsa;
65 			return 0;
66 		}
67 	}
68 	fprintf(bat->err, _("Invalid format!\n"));
69 	return -EINVAL;
70 }
71 
set_snd_pcm_params(struct bat * bat,struct pcm_container * sndpcm)72 static int set_snd_pcm_params(struct bat *bat, struct pcm_container *sndpcm)
73 {
74 	snd_pcm_hw_params_t *params;
75 	snd_pcm_format_t format;
76 	unsigned int buffer_time = 0;
77 	unsigned int period_time = 0;
78 	snd_pcm_uframes_t buffer_size = 0;
79 	snd_pcm_uframes_t period_size = 0;
80 	unsigned int rate;
81 	int err;
82 	const char *device_name = snd_pcm_name(sndpcm->handle);
83 
84 	/* Convert common format to ALSA format */
85 	err = format_convert(bat, &format);
86 	if (err != 0)
87 		return err;
88 
89 	/* Allocate a hardware parameters object. */
90 	snd_pcm_hw_params_alloca(&params);
91 
92 	/* Fill it in with default values. */
93 	err = snd_pcm_hw_params_any(sndpcm->handle, params);
94 	if (err < 0) {
95 		fprintf(bat->err, _("Set parameter to device error: "));
96 		fprintf(bat->err, _("default params: %s: %s(%d)\n"),
97 				device_name, snd_strerror(err), err);
98 		return err;
99 	}
100 
101 	/* Set access mode */
102 	err = snd_pcm_hw_params_set_access(sndpcm->handle, params,
103 			SND_PCM_ACCESS_RW_INTERLEAVED);
104 	if (err < 0) {
105 		fprintf(bat->err, _("Set parameter to device error: "));
106 		fprintf(bat->err, _("access type: %s: %s(%d)\n"),
107 				device_name, snd_strerror(err), err);
108 		return err;
109 	}
110 
111 	/* Set format */
112 	err = snd_pcm_hw_params_set_format(sndpcm->handle, params, format);
113 	if (err < 0) {
114 		fprintf(bat->err, _("Set parameter to device error: "));
115 		fprintf(bat->err, _("PCM format: %d %s: %s(%d)\n"), format,
116 				device_name, snd_strerror(err), err);
117 		return err;
118 	}
119 
120 	/* Set channels */
121 	err = snd_pcm_hw_params_set_channels(sndpcm->handle,
122 			params, bat->channels);
123 	if (err < 0) {
124 		fprintf(bat->err, _("Set parameter to device error: "));
125 		fprintf(bat->err, _("channel number: %d %s: %s(%d)\n"),
126 				bat->channels,
127 				device_name, snd_strerror(err), err);
128 		return err;
129 	}
130 
131 	/* Set sampling rate */
132 	rate = bat->rate;
133 	err = snd_pcm_hw_params_set_rate_near(sndpcm->handle,
134 			params, &bat->rate,
135 			0);
136 	if (err < 0) {
137 		fprintf(bat->err, _("Set parameter to device error: "));
138 		fprintf(bat->err, _("sample rate: %d %s: %s(%d)\n"),
139 				bat->rate,
140 				device_name, snd_strerror(err), err);
141 		return err;
142 	}
143 	if ((float) rate * (1 + RATE_RANGE) < bat->rate
144 			|| (float) rate * (1 - RATE_RANGE) > bat->rate) {
145 		fprintf(bat->err, _("Invalid parameters: sample rate: "));
146 		fprintf(bat->err, _("requested %dHz, got %dHz\n"),
147 				rate, bat->rate);
148 		return -EINVAL;
149 	}
150 
151 	if (bat->buffer_size > 0 && bat->period_size == 0)
152 		bat->period_size = bat->buffer_size / DIV_BUFFERSIZE;
153 
154 	if (bat->roundtriplatency && bat->buffer_size == 0) {
155 		/* Set to minimum buffer size and period size
156 		   for latency test */
157 		if (snd_pcm_hw_params_get_buffer_size_min(params,
158 				&buffer_size) < 0) {
159 			fprintf(bat->err,
160 					_("Get parameter from device error: "));
161 			fprintf(bat->err, _("buffer size min: %d %s: %s(%d)\n"),
162 					(int) buffer_size,
163 					device_name, snd_strerror(err), err);
164 			return -EINVAL;
165 		}
166 
167 		if (snd_pcm_hw_params_get_period_size_min(params,
168 				&period_size, 0) < 0) {
169 			fprintf(bat->err,
170 					_("Get parameter from device error: "));
171 			fprintf(bat->err, _("period size min: %d %s: %s(%d)\n"),
172 					(int) period_size,
173 					device_name, snd_strerror(err), err);
174 			return -EINVAL;
175 		}
176 		bat->buffer_size = (int) buffer_size;
177 		bat->period_size = (int) period_size;
178 	}
179 
180 	if (bat->buffer_size > 0) {
181 		buffer_size = bat->buffer_size;
182 		period_size = bat->period_size;
183 
184 		fprintf(bat->log, _("Set period size: %d  buffer size: %d\n"),
185 				(int) period_size, (int) buffer_size);
186 
187 		err = snd_pcm_hw_params_set_buffer_size_near(sndpcm->handle,
188 				params, &buffer_size);
189 		if (err < 0) {
190 			fprintf(bat->err, _("Set parameter to device error: "));
191 			fprintf(bat->err, _("buffer size: %d %s: %s(%d)\n"),
192 					(int) buffer_size,
193 					device_name, snd_strerror(err), err);
194 			return err;
195 		}
196 
197 		err = snd_pcm_hw_params_set_period_size_near(sndpcm->handle,
198 				params, &period_size, 0);
199 		if (err < 0) {
200 			fprintf(bat->err, _("Set parameter to device error: "));
201 			fprintf(bat->err, _("period size: %d %s: %s(%d)\n"),
202 					(int) period_size,
203 					device_name, snd_strerror(err), err);
204 			return err;
205 		}
206 	} else {
207 		if (snd_pcm_hw_params_get_buffer_time_max(params,
208 				&buffer_time, 0) < 0) {
209 			fprintf(bat->err,
210 					_("Get parameter from device error: "));
211 			fprintf(bat->err, _("buffer time: %d %s: %s(%d)\n"),
212 					buffer_time,
213 					device_name, snd_strerror(err), err);
214 			return -EINVAL;
215 		}
216 
217 		if (buffer_time > MAX_BUFFERTIME)
218 			buffer_time = MAX_BUFFERTIME;
219 
220 		period_time = buffer_time / DIV_BUFFERTIME;
221 
222 		/* Set buffer time and period time */
223 		err = snd_pcm_hw_params_set_buffer_time_near(sndpcm->handle,
224 				params, &buffer_time, 0);
225 		if (err < 0) {
226 			fprintf(bat->err, _("Set parameter to device error: "));
227 			fprintf(bat->err, _("buffer time: %d %s: %s(%d)\n"),
228 					buffer_time,
229 					device_name, snd_strerror(err), err);
230 			return err;
231 		}
232 
233 		err = snd_pcm_hw_params_set_period_time_near(sndpcm->handle,
234 				params, &period_time, 0);
235 		if (err < 0) {
236 			fprintf(bat->err, _("Set parameter to device error: "));
237 			fprintf(bat->err, _("period time: %d %s: %s(%d)\n"),
238 					period_time,
239 					device_name, snd_strerror(err), err);
240 			return err;
241 		}
242 	}
243 
244 	/* Write the parameters to the driver */
245 	if (snd_pcm_hw_params(sndpcm->handle, params) < 0) {
246 		fprintf(bat->err, _("Set parameter to device error: "));
247 		fprintf(bat->err, _("hw params: %s: %s(%d)\n"),
248 				device_name, snd_strerror(err), err);
249 		return -EINVAL;
250 	}
251 
252 	err = snd_pcm_hw_params_get_period_size(params,
253 			&sndpcm->period_size, 0);
254 	if (err < 0) {
255 		fprintf(bat->err, _("Get parameter from device error: "));
256 		fprintf(bat->err, _("period size: %zd %s: %s(%d)\n"),
257 				sndpcm->period_size,
258 				device_name, snd_strerror(err), err);
259 		return err;
260 	}
261 
262 	err = snd_pcm_hw_params_get_buffer_size(params, &sndpcm->buffer_size);
263 	if (err < 0) {
264 		fprintf(bat->err, _("Get parameter from device error: "));
265 		fprintf(bat->err, _("buffer size: %zd %s: %s(%d)\n"),
266 				sndpcm->buffer_size,
267 				device_name, snd_strerror(err), err);
268 		return err;
269 	}
270 
271 	if (sndpcm->period_size == sndpcm->buffer_size) {
272 		fprintf(bat->err, _("Invalid parameters: can't use period "));
273 		fprintf(bat->err, _("equal to buffer size (%zd)\n"),
274 				sndpcm->period_size);
275 		return -EINVAL;
276 	}
277 
278 	fprintf(bat->log, _("Get period size: %d  buffer size: %d\n"),
279 			(int) sndpcm->period_size, (int) sndpcm->buffer_size);
280 
281 	err = snd_pcm_format_physical_width(format);
282 	if (err < 0) {
283 		fprintf(bat->err, _("Invalid parameters: "));
284 		fprintf(bat->err, _("snd_pcm_format_physical_width: %d\n"),
285 				err);
286 		return err;
287 	}
288 	sndpcm->sample_bits = err;
289 
290 	sndpcm->frame_bits = sndpcm->sample_bits * bat->channels;
291 
292 	/* Calculate the period bytes */
293 	sndpcm->period_bytes = sndpcm->period_size * sndpcm->frame_bits / 8;
294 	sndpcm->buffer = (char *) malloc(sndpcm->period_bytes);
295 	if (sndpcm->buffer == NULL) {
296 		fprintf(bat->err, _("Not enough memory: size=%zd\n"),
297 				sndpcm->period_bytes);
298 		return -ENOMEM;
299 	}
300 
301 	return 0;
302 }
303 
write_to_pcm(const struct pcm_container * sndpcm,int frames,struct bat * bat)304 static int write_to_pcm(const struct pcm_container *sndpcm,
305 		int frames, struct bat *bat)
306 {
307 	int err;
308 	int offset = 0;
309 	int remain = frames;
310 
311 	while (remain > 0) {
312 		err = snd_pcm_writei(sndpcm->handle, sndpcm->buffer + offset,
313 				remain);
314 		if (err == -EAGAIN || (err >= 0 && err < frames)) {
315 			snd_pcm_wait(sndpcm->handle, 500);
316 		} else if (err == -EPIPE) {
317 			fprintf(bat->err, _("Underrun: %s(%d)\n"),
318 					snd_strerror(err), err);
319 			if (bat->roundtriplatency)
320 				bat->latency.xrun_error = true;
321 			snd_pcm_prepare(sndpcm->handle);
322 		} else if (err == -ESTRPIPE) {
323 			while ((err = snd_pcm_resume(sndpcm->handle)) == -EAGAIN)
324 				sleep(1);  /* wait until resume flag is released */
325 			if (err < 0)
326 				snd_pcm_prepare(sndpcm->handle);
327 		} else if (err < 0) {
328 			fprintf(bat->err, _("Write PCM device error: %s(%d)\n"),
329 					snd_strerror(err), err);
330 			return err;
331 		}
332 
333 		if (err > 0) {
334 			remain -= err;
335 			offset += err * sndpcm->frame_bits / 8;
336 		}
337 	}
338 
339 	return 0;
340 }
341 
342 /**
343  * Process output data for latency test
344  */
latencytest_process_output(struct pcm_container * sndpcm,struct bat * bat)345 static int latencytest_process_output(struct pcm_container *sndpcm,
346 		struct bat *bat)
347 {
348 	int err = 0;
349 	int bytes = sndpcm->period_bytes; /* playback buffer size */
350 	int frames = sndpcm->period_size; /* frame count */
351 
352 	bat->latency.is_playing = true;
353 
354 	while (1) {
355 		/* generate output data */
356 		err = handleoutput(bat, sndpcm->buffer, bytes, frames);
357 		if (err != 0)
358 			break;
359 
360 		err = write_to_pcm(sndpcm, frames, bat);
361 		if (err != 0)
362 			break;
363 
364 		/* Xrun error, terminate the playback thread*/
365 		if (bat->latency.xrun_error == true)
366 			break;
367 
368 		if (bat->latency.state == LATENCY_STATE_COMPLETE_SUCCESS)
369 			break;
370 
371 		bat->periods_played++;
372 	}
373 
374 	bat->latency.is_playing = false;
375 
376 	return err;
377 }
378 
write_to_pcm_loop(struct pcm_container * sndpcm,struct bat * bat)379 static int write_to_pcm_loop(struct pcm_container *sndpcm, struct bat *bat)
380 {
381 	int err = 0;
382 	int bytes = sndpcm->period_bytes; /* playback buffer size */
383 	int frames = bytes * 8 / sndpcm->frame_bits; /* frame count */
384 	FILE *fp = NULL;
385 	int bytes_total = 0;
386 
387 	if (bat->debugplay) {
388 		fp = fopen(bat->debugplay, "wb");
389 		err = -errno;
390 		if (fp == NULL) {
391 			fprintf(bat->err, _("Cannot open file: %s %d\n"),
392 					bat->debugplay, err);
393 			return err;
394 		}
395 		/* leave space for wav header */
396 		if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
397 			err = -errno;
398 			fclose(fp);
399 			return err;
400 		}
401 	}
402 
403 	while (1) {
404 		err = generate_input_data(bat, sndpcm->buffer, bytes, frames);
405 		if (err != 0)
406 			break;
407 
408 		if (bat->debugplay) {
409 			if (fwrite(sndpcm->buffer, 1, bytes, fp) != bytes) {
410 				err = -EIO;
411 				break;
412 			}
413 			bytes_total += bytes;
414 		}
415 
416 		bat->periods_played++;
417 		if (bat->period_is_limited
418 				&& bat->periods_played >= bat->periods_total)
419 			break;
420 
421 		err = write_to_pcm(sndpcm, frames, bat);
422 		if (err != 0)
423 			break;
424 	}
425 
426 	if (bat->debugplay) {
427 		update_wav_header(bat, fp, bytes_total);
428 		fclose(fp);
429 	}
430 
431 	snd_pcm_drain(sndpcm->handle);
432 
433 	return err;
434 }
435 
436 /**
437  * Play
438  */
playback_alsa(struct bat * bat)439 void *playback_alsa(struct bat *bat)
440 {
441 	int err = 0;
442 	struct pcm_container sndpcm;
443 
444 	fprintf(bat->log, _("Entering playback thread (ALSA).\n"));
445 
446 	retval_play = 0;
447 	memset(&sndpcm, 0, sizeof(sndpcm));
448 
449 	err = snd_pcm_open(&sndpcm.handle, bat->playback.device,
450 			SND_PCM_STREAM_PLAYBACK, 0);
451 	if (err != 0) {
452 		fprintf(bat->err, _("Cannot open PCM playback device: "));
453 		fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
454 		retval_play = err;
455 		goto exit1;
456 	}
457 
458 	err = set_snd_pcm_params(bat, &sndpcm);
459 	if (err != 0) {
460 		retval_play = err;
461 		goto exit2;
462 	}
463 
464 	if (bat->playback.file == NULL) {
465 		fprintf(bat->log, _("Playing generated audio sine wave"));
466 		bat->sinus_duration == 0 ?
467 			fprintf(bat->log, _(" endlessly\n")) :
468 			fprintf(bat->log, _("\n"));
469 	} else {
470 		fprintf(bat->log, _("Playing input audio file: %s\n"),
471 				bat->playback.file);
472 		bat->fp = fopen(bat->playback.file, "rb");
473 		err = -errno;
474 		if (bat->fp == NULL) {
475 			fprintf(bat->err, _("Cannot open file: %s %d\n"),
476 					bat->playback.file, err);
477 			retval_play = err;
478 			goto exit3;
479 		}
480 		/* Skip header */
481 		err = read_wav_header(bat, bat->playback.file, bat->fp, true);
482 		if (err != 0) {
483 			retval_play = err;
484 			goto exit4;
485 		}
486 	}
487 
488 	if (bat->roundtriplatency)
489 		err = latencytest_process_output(&sndpcm, bat);
490 	else
491 		err = write_to_pcm_loop(&sndpcm, bat);
492 	if (err < 0) {
493 		retval_play = err;
494 		goto exit4;
495 	}
496 
497 exit4:
498 	if (bat->playback.file)
499 		fclose(bat->fp);
500 exit3:
501 	free(sndpcm.buffer);
502 exit2:
503 	snd_pcm_close(sndpcm.handle);
504 exit1:
505 	pthread_exit(&retval_play);
506 }
507 
read_from_pcm(struct pcm_container * sndpcm,int frames,struct bat * bat)508 static int read_from_pcm(struct pcm_container *sndpcm,
509 		int frames, struct bat *bat)
510 {
511 	int err = 0;
512 	int offset = 0;
513 	int remain = frames;
514 
515 	while (remain > 0) {
516 		err = snd_pcm_readi(sndpcm->handle,
517 				sndpcm->buffer + offset, remain);
518 		if (err == -EAGAIN || (err >= 0 && err < remain)) {
519 			snd_pcm_wait(sndpcm->handle, 500);
520 		} else if (err == -EPIPE) {
521 			snd_pcm_prepare(sndpcm->handle);
522 			fprintf(bat->err, _("Overrun: %s(%d)\n"),
523 					snd_strerror(err), err);
524 			if (bat->roundtriplatency)
525 				bat->latency.xrun_error = true;
526 		} else if (err == -ESTRPIPE) {
527 			while ((err = snd_pcm_resume(sndpcm->handle)) == -EAGAIN)
528 				sleep(1);  /* wait until resume flag is released */
529 			if (err < 0)
530 				snd_pcm_prepare(sndpcm->handle);
531 		} else if (err < 0) {
532 			fprintf(bat->err, _("Read PCM device error: %s(%d)\n"),
533 					snd_strerror(err), err);
534 			return err;
535 		}
536 
537 		if (err > 0) {
538 			remain -= err;
539 			offset += err * sndpcm->frame_bits / 8;
540 		}
541 	}
542 
543 	return 0;
544 }
545 
read_from_pcm_loop(struct pcm_container * sndpcm,struct bat * bat)546 static int read_from_pcm_loop(struct pcm_container *sndpcm, struct bat *bat)
547 {
548 	int err = 0;
549 	FILE *fp = NULL;
550 	int size, frames;
551 	int bytes_read = 0;
552 	int bytes_count = bat->frames * bat->frame_size;
553 	int remain = bytes_count;
554 
555 	remove(bat->capture.file);
556 	fp = fopen(bat->capture.file, "wb");
557 	err = -errno;
558 	if (fp == NULL) {
559 		fprintf(bat->err, _("Cannot open file: %s %d\n"),
560 				bat->capture.file, err);
561 		return err;
562 	}
563 	/* leave space for file header */
564 	if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
565 		err = -errno;
566 		fclose(fp);
567 		return err;
568 	}
569 
570 	while (remain > 0) {
571 		size = (remain <= sndpcm->period_bytes) ?
572 			remain : sndpcm->period_bytes;
573 		frames = size * 8 / sndpcm->frame_bits;
574 
575 		/* read a chunk from pcm device */
576 		err = read_from_pcm(sndpcm, frames, bat);
577 		if (err != 0)
578 			break;
579 
580 		/* write the chunk to file */
581 		if (fwrite(sndpcm->buffer, 1, size, fp) != size) {
582 			err = -EIO;
583 			break;
584 		}
585 
586 		bytes_read += size;
587 		remain -= size;
588 		bat->periods_played++;
589 
590 		if (bat->period_is_limited
591 				&& bat->periods_played >= bat->periods_total)
592 			break;
593 	}
594 
595 	update_wav_header(bat, fp, bytes_read);
596 
597 	fclose(fp);
598 	return err;
599 }
600 
601 /**
602  * Process input data for latency test
603  */
latencytest_process_input(struct pcm_container * sndpcm,struct bat * bat)604 static int latencytest_process_input(struct pcm_container *sndpcm,
605 		struct bat *bat)
606 {
607 	int err = 0;
608 	FILE *fp = NULL;
609 	int bytes_read = 0;
610 	int frames = sndpcm->period_size;
611 	int size = sndpcm->period_bytes;
612 	int bytes_count = bat->frames * bat->frame_size;
613 
614 	remove(bat->capture.file);
615 	fp = fopen(bat->capture.file, "wb");
616 	err = -errno;
617 	if (fp == NULL) {
618 		fprintf(bat->err, _("Cannot open file: %s %d\n"),
619 				bat->capture.file, err);
620 		return err;
621 	}
622 	/* leave space for file header */
623 	if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
624 		fclose(fp);
625 		return err;
626 	}
627 
628 	bat->latency.is_capturing = true;
629 
630 	while (bytes_read < bytes_count) {
631 		/* read a chunk from pcm device */
632 		err = read_from_pcm(sndpcm, frames, bat);
633 		if (err != 0)
634 			break;
635 
636 		/* Xrun error, terminate the capture thread*/
637 		if (bat->latency.xrun_error == true)
638 			break;
639 
640 		err = handleinput(bat, sndpcm->buffer, frames);
641 		if (err != 0)
642 			break;
643 
644 		if (bat->latency.is_playing == false)
645 			break;
646 
647 		/* write the chunk to file */
648 		if (fwrite(sndpcm->buffer, 1, size, fp) != size) {
649 			err = -EIO;
650 			break;
651 		}
652 
653 		bytes_read += size;
654 	}
655 
656 	bat->latency.is_capturing = false;
657 
658 	update_wav_header(bat, fp, bytes_read);
659 
660 	fclose(fp);
661 	return err;
662 }
663 
664 
pcm_cleanup(void * p)665 static void pcm_cleanup(void *p)
666 {
667 	snd_pcm_close(p);
668 }
669 
670 /**
671  * Record
672  */
record_alsa(struct bat * bat)673 void *record_alsa(struct bat *bat)
674 {
675 	int err = 0;
676 	struct pcm_container sndpcm;
677 
678 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
679 
680 	fprintf(bat->log, _("Entering capture thread (ALSA).\n"));
681 
682 	retval_record = 0;
683 	memset(&sndpcm, 0, sizeof(sndpcm));
684 
685 	err = snd_pcm_open(&sndpcm.handle, bat->capture.device,
686 			SND_PCM_STREAM_CAPTURE, 0);
687 	if (err != 0) {
688 		fprintf(bat->err, _("Cannot open PCM capture device: "));
689 		fprintf(bat->err, _("%s(%d)\n"), snd_strerror(err), err);
690 		retval_record = err;
691 		goto exit1;
692 	}
693 
694 	err = set_snd_pcm_params(bat, &sndpcm);
695 	if (err != 0) {
696 		retval_record = err;
697 		goto exit2;
698 	}
699 
700 	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
701 	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
702 	pthread_cleanup_push(pcm_cleanup, sndpcm.handle);
703 	pthread_cleanup_push(free, sndpcm.buffer);
704 
705 	fprintf(bat->log, _("Recording ...\n"));
706 	if (bat->roundtriplatency)
707 		err = latencytest_process_input(&sndpcm, bat);
708 	else
709 		err = read_from_pcm_loop(&sndpcm, bat);
710 
711 	pthread_cleanup_pop(0);
712 	pthread_cleanup_pop(0);
713 
714 	if (err != 0) {
715 		retval_record = err;
716 		goto exit3;
717 	}
718 
719 	/* Normally we will never reach this part of code (unless error in
720 	 * previous call) (before exit3) as this thread will be cancelled
721 	 * by end of play thread. Except in single line mode. */
722 	snd_pcm_drain(sndpcm.handle);
723 	pthread_exit(&retval_record);
724 
725 exit3:
726 	free(sndpcm.buffer);
727 exit2:
728 	snd_pcm_close(sndpcm.handle);
729 exit1:
730 	pthread_exit(&retval_record);
731 }
732