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