tarina

git clone https://git.tarina.org/tarina
Log | Files | Refs | README | LICENSE

alsa.c (18179B)


      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 
     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 
     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 
    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  */
    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 
    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  */
    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 
    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 
    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  */
    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 
    665 static void pcm_cleanup(void *p)
    666 {
    667 	snd_pcm_close(p);
    668 }
    669 
    670 /**
    671  * Record
    672  */
    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 	if (err != 0) {
    711 		retval_record = err;
    712 		goto exit3;
    713 	}
    714 
    715 	/* Normally we will never reach this part of code (unless error in
    716 	 * previous call) (before exit3) as this thread will be cancelled
    717 	 * by end of play thread. Except in single line mode. */
    718 	pthread_cleanup_pop(0);
    719 	pthread_cleanup_pop(0);
    720 
    721 	snd_pcm_drain(sndpcm.handle);
    722 	pthread_exit(&retval_record);
    723 
    724 exit3:
    725 	free(sndpcm.buffer);
    726 exit2:
    727 	snd_pcm_close(sndpcm.handle);
    728 exit1:
    729 	pthread_exit(&retval_record);
    730 }