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 <stdlib.h>
22c72fcc34Sopenharmony_ci#include <pthread.h>
23c72fcc34Sopenharmony_ci#include <errno.h>
24c72fcc34Sopenharmony_ci
25c72fcc34Sopenharmony_ci#include <tinyalsa/asoundlib.h>
26c72fcc34Sopenharmony_ci
27c72fcc34Sopenharmony_ci#include "gettext.h"
28c72fcc34Sopenharmony_ci
29c72fcc34Sopenharmony_ci#include "common.h"
30c72fcc34Sopenharmony_ci#include "tinyalsa.h"
31c72fcc34Sopenharmony_ci#include "latencytest.h"
32c72fcc34Sopenharmony_ci
33c72fcc34Sopenharmony_cistruct format_map_table {
34c72fcc34Sopenharmony_ci	enum _bat_pcm_format format_bat;
35c72fcc34Sopenharmony_ci	enum pcm_format format_tiny;
36c72fcc34Sopenharmony_ci};
37c72fcc34Sopenharmony_ci
38c72fcc34Sopenharmony_cistatic struct format_map_table map_tables[] = {
39c72fcc34Sopenharmony_ci	{ BAT_PCM_FORMAT_S16_LE, PCM_FORMAT_S16_LE },
40c72fcc34Sopenharmony_ci	{ BAT_PCM_FORMAT_S32_LE, PCM_FORMAT_S32_LE },
41c72fcc34Sopenharmony_ci	{ BAT_PCM_FORMAT_MAX, },
42c72fcc34Sopenharmony_ci};
43c72fcc34Sopenharmony_ci
44c72fcc34Sopenharmony_cistatic int format_convert(struct bat *bat, struct pcm_config *config)
45c72fcc34Sopenharmony_ci{
46c72fcc34Sopenharmony_ci	struct format_map_table *t = map_tables;
47c72fcc34Sopenharmony_ci
48c72fcc34Sopenharmony_ci	for (; t->format_bat != BAT_PCM_FORMAT_MAX; t++) {
49c72fcc34Sopenharmony_ci		if (t->format_bat == bat->format) {
50c72fcc34Sopenharmony_ci			config->format = t->format_tiny;
51c72fcc34Sopenharmony_ci			return 0;
52c72fcc34Sopenharmony_ci		}
53c72fcc34Sopenharmony_ci	}
54c72fcc34Sopenharmony_ci	fprintf(bat->err, _("Invalid format!\n"));
55c72fcc34Sopenharmony_ci	return -EINVAL;
56c72fcc34Sopenharmony_ci}
57c72fcc34Sopenharmony_ci
58c72fcc34Sopenharmony_cistatic int init_config(struct bat *bat, struct pcm_config *config)
59c72fcc34Sopenharmony_ci{
60c72fcc34Sopenharmony_ci	config->channels = bat->channels;
61c72fcc34Sopenharmony_ci	config->rate = bat->rate;
62c72fcc34Sopenharmony_ci	if (bat->period_size > 0)
63c72fcc34Sopenharmony_ci		config->period_size = bat->period_size;
64c72fcc34Sopenharmony_ci	else
65c72fcc34Sopenharmony_ci		config->period_size = TINYALSA_PERIODSIZE;
66c72fcc34Sopenharmony_ci	config->period_count = 4;
67c72fcc34Sopenharmony_ci	config->start_threshold = 0;
68c72fcc34Sopenharmony_ci	config->stop_threshold = 0;
69c72fcc34Sopenharmony_ci	config->silence_threshold = 0;
70c72fcc34Sopenharmony_ci
71c72fcc34Sopenharmony_ci	return format_convert(bat, config);
72c72fcc34Sopenharmony_ci}
73c72fcc34Sopenharmony_ci
74c72fcc34Sopenharmony_ci/**
75c72fcc34Sopenharmony_ci * Called when thread is finished
76c72fcc34Sopenharmony_ci */
77c72fcc34Sopenharmony_cistatic void close_handle(void *handle)
78c72fcc34Sopenharmony_ci{
79c72fcc34Sopenharmony_ci	struct pcm *pcm = handle;
80c72fcc34Sopenharmony_ci
81c72fcc34Sopenharmony_ci	if (NULL != pcm)
82c72fcc34Sopenharmony_ci		pcm_close(pcm);
83c72fcc34Sopenharmony_ci}
84c72fcc34Sopenharmony_ci
85c72fcc34Sopenharmony_ci/**
86c72fcc34Sopenharmony_ci * Check that a parameter is inside bounds
87c72fcc34Sopenharmony_ci */
88c72fcc34Sopenharmony_cistatic int check_param(struct bat *bat, struct pcm_params *params,
89c72fcc34Sopenharmony_ci		unsigned int param, unsigned int value,
90c72fcc34Sopenharmony_ci		char *param_name, char *param_unit)
91c72fcc34Sopenharmony_ci{
92c72fcc34Sopenharmony_ci	unsigned int min;
93c72fcc34Sopenharmony_ci	unsigned int max;
94c72fcc34Sopenharmony_ci	int ret = 0;
95c72fcc34Sopenharmony_ci
96c72fcc34Sopenharmony_ci	min = pcm_params_get_min(params, param);
97c72fcc34Sopenharmony_ci	if (value < min) {
98c72fcc34Sopenharmony_ci		fprintf(bat->err,
99c72fcc34Sopenharmony_ci			_("%s is %u%s, device only supports >= %u%s!\n"),
100c72fcc34Sopenharmony_ci			param_name, value, param_unit, min, param_unit);
101c72fcc34Sopenharmony_ci		ret = -EINVAL;
102c72fcc34Sopenharmony_ci	}
103c72fcc34Sopenharmony_ci
104c72fcc34Sopenharmony_ci	max = pcm_params_get_max(params, param);
105c72fcc34Sopenharmony_ci	if (value > max) {
106c72fcc34Sopenharmony_ci		fprintf(bat->err,
107c72fcc34Sopenharmony_ci			_("%s is %u%s, device only supports <= %u%s!\n"),
108c72fcc34Sopenharmony_ci			param_name, value, param_unit, max, param_unit);
109c72fcc34Sopenharmony_ci		ret = -EINVAL;
110c72fcc34Sopenharmony_ci	}
111c72fcc34Sopenharmony_ci
112c72fcc34Sopenharmony_ci	return ret;
113c72fcc34Sopenharmony_ci}
114c72fcc34Sopenharmony_ci
115c72fcc34Sopenharmony_ci/**
116c72fcc34Sopenharmony_ci * Check all parameters
117c72fcc34Sopenharmony_ci */
118c72fcc34Sopenharmony_cistatic int check_playback_params(struct bat *bat,
119c72fcc34Sopenharmony_ci		struct pcm_config *config)
120c72fcc34Sopenharmony_ci{
121c72fcc34Sopenharmony_ci	struct pcm_params *params;
122c72fcc34Sopenharmony_ci	unsigned int card = bat->playback.card_tiny;
123c72fcc34Sopenharmony_ci	unsigned int device = bat->playback.device_tiny;
124c72fcc34Sopenharmony_ci	int err = 0;
125c72fcc34Sopenharmony_ci
126c72fcc34Sopenharmony_ci	params = pcm_params_get(card, device, PCM_OUT);
127c72fcc34Sopenharmony_ci	if (params == NULL) {
128c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Unable to open PCM device %u!\n"),
129c72fcc34Sopenharmony_ci				device);
130c72fcc34Sopenharmony_ci		return -EINVAL;
131c72fcc34Sopenharmony_ci	}
132c72fcc34Sopenharmony_ci
133c72fcc34Sopenharmony_ci	err = check_param(bat, params, PCM_PARAM_RATE,
134c72fcc34Sopenharmony_ci			config->rate, "Sample rate", "Hz");
135c72fcc34Sopenharmony_ci	if (err < 0)
136c72fcc34Sopenharmony_ci		goto exit;
137c72fcc34Sopenharmony_ci	err = check_param(bat, params, PCM_PARAM_CHANNELS,
138c72fcc34Sopenharmony_ci			config->channels, "Sample", " channels");
139c72fcc34Sopenharmony_ci	if (err < 0)
140c72fcc34Sopenharmony_ci		goto exit;
141c72fcc34Sopenharmony_ci	err = check_param(bat, params, PCM_PARAM_SAMPLE_BITS,
142c72fcc34Sopenharmony_ci			bat->sample_size * 8, "Bitrate", " bits");
143c72fcc34Sopenharmony_ci	if (err < 0)
144c72fcc34Sopenharmony_ci		goto exit;
145c72fcc34Sopenharmony_ci	err = check_param(bat, params, PCM_PARAM_PERIOD_SIZE,
146c72fcc34Sopenharmony_ci			config->period_size, "Period size", "Hz");
147c72fcc34Sopenharmony_ci	if (err < 0)
148c72fcc34Sopenharmony_ci		goto exit;
149c72fcc34Sopenharmony_ci	err = check_param(bat, params, PCM_PARAM_PERIODS,
150c72fcc34Sopenharmony_ci			config->period_count, "Period count", "Hz");
151c72fcc34Sopenharmony_ci	if (err < 0)
152c72fcc34Sopenharmony_ci		goto exit;
153c72fcc34Sopenharmony_ci
154c72fcc34Sopenharmony_ciexit:
155c72fcc34Sopenharmony_ci	pcm_params_free(params);
156c72fcc34Sopenharmony_ci
157c72fcc34Sopenharmony_ci	return err;
158c72fcc34Sopenharmony_ci}
159c72fcc34Sopenharmony_ci
160c72fcc34Sopenharmony_ci/**
161c72fcc34Sopenharmony_ci * Process output data for latency test
162c72fcc34Sopenharmony_ci */
163c72fcc34Sopenharmony_cistatic int latencytest_process_output(struct bat *bat, struct pcm *pcm,
164c72fcc34Sopenharmony_ci		void *buffer, int bytes)
165c72fcc34Sopenharmony_ci{
166c72fcc34Sopenharmony_ci	int err = 0;
167c72fcc34Sopenharmony_ci	int frames = bytes / bat->frame_size;
168c72fcc34Sopenharmony_ci
169c72fcc34Sopenharmony_ci	fprintf(bat->log, _("Play sample with %d frames buffer\n"), frames);
170c72fcc34Sopenharmony_ci
171c72fcc34Sopenharmony_ci	bat->latency.is_playing = true;
172c72fcc34Sopenharmony_ci
173c72fcc34Sopenharmony_ci	while (1) {
174c72fcc34Sopenharmony_ci		/* generate output data */
175c72fcc34Sopenharmony_ci		err = handleoutput(bat, buffer, bytes, frames);
176c72fcc34Sopenharmony_ci		if (err != 0)
177c72fcc34Sopenharmony_ci			break;
178c72fcc34Sopenharmony_ci
179c72fcc34Sopenharmony_ci		err = pcm_write(pcm, buffer, bytes);
180c72fcc34Sopenharmony_ci		if (err != 0)
181c72fcc34Sopenharmony_ci			break;
182c72fcc34Sopenharmony_ci
183c72fcc34Sopenharmony_ci		if (bat->latency.state == LATENCY_STATE_COMPLETE_SUCCESS)
184c72fcc34Sopenharmony_ci			break;
185c72fcc34Sopenharmony_ci
186c72fcc34Sopenharmony_ci		bat->periods_played++;
187c72fcc34Sopenharmony_ci	}
188c72fcc34Sopenharmony_ci
189c72fcc34Sopenharmony_ci	bat->latency.is_playing = false;
190c72fcc34Sopenharmony_ci
191c72fcc34Sopenharmony_ci	return err;
192c72fcc34Sopenharmony_ci}
193c72fcc34Sopenharmony_ci
194c72fcc34Sopenharmony_ci/**
195c72fcc34Sopenharmony_ci * Play sample
196c72fcc34Sopenharmony_ci */
197c72fcc34Sopenharmony_cistatic int play_sample(struct bat *bat, struct pcm *pcm,
198c72fcc34Sopenharmony_ci		void *buffer, int bytes)
199c72fcc34Sopenharmony_ci{
200c72fcc34Sopenharmony_ci	int err = 0;
201c72fcc34Sopenharmony_ci	int frames = bytes / bat->frame_size;
202c72fcc34Sopenharmony_ci	FILE *fp = NULL;
203c72fcc34Sopenharmony_ci	int bytes_total = 0;
204c72fcc34Sopenharmony_ci
205c72fcc34Sopenharmony_ci	if (bat->debugplay) {
206c72fcc34Sopenharmony_ci		fp = fopen(bat->debugplay, "wb");
207c72fcc34Sopenharmony_ci		err = -errno;
208c72fcc34Sopenharmony_ci		if (fp == NULL) {
209c72fcc34Sopenharmony_ci			fprintf(bat->err, _("Cannot open file: %s %d\n"),
210c72fcc34Sopenharmony_ci					bat->debugplay, err);
211c72fcc34Sopenharmony_ci			return err;
212c72fcc34Sopenharmony_ci		}
213c72fcc34Sopenharmony_ci		/* leave space for file header */
214c72fcc34Sopenharmony_ci		if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
215c72fcc34Sopenharmony_ci			err = -errno;
216c72fcc34Sopenharmony_ci			fclose(fp);
217c72fcc34Sopenharmony_ci			return err;
218c72fcc34Sopenharmony_ci		}
219c72fcc34Sopenharmony_ci	}
220c72fcc34Sopenharmony_ci
221c72fcc34Sopenharmony_ci	while (1) {
222c72fcc34Sopenharmony_ci		err = generate_input_data(bat, buffer, bytes, frames);
223c72fcc34Sopenharmony_ci		if (err != 0)
224c72fcc34Sopenharmony_ci			break;
225c72fcc34Sopenharmony_ci
226c72fcc34Sopenharmony_ci		if (bat->debugplay) {
227c72fcc34Sopenharmony_ci			if (fwrite(buffer, 1, bytes, fp) != bytes) {
228c72fcc34Sopenharmony_ci				err = -EIO;
229c72fcc34Sopenharmony_ci				break;
230c72fcc34Sopenharmony_ci			}
231c72fcc34Sopenharmony_ci			bytes_total += bytes;
232c72fcc34Sopenharmony_ci		}
233c72fcc34Sopenharmony_ci
234c72fcc34Sopenharmony_ci		bat->periods_played++;
235c72fcc34Sopenharmony_ci		if (bat->period_is_limited
236c72fcc34Sopenharmony_ci				&& bat->periods_played >= bat->periods_total)
237c72fcc34Sopenharmony_ci			break;
238c72fcc34Sopenharmony_ci
239c72fcc34Sopenharmony_ci		err = pcm_write(pcm, buffer, bytes);
240c72fcc34Sopenharmony_ci		if (err != 0)
241c72fcc34Sopenharmony_ci			break;
242c72fcc34Sopenharmony_ci	}
243c72fcc34Sopenharmony_ci
244c72fcc34Sopenharmony_ci	if (bat->debugplay) {
245c72fcc34Sopenharmony_ci		update_wav_header(bat, fp, bytes_total);
246c72fcc34Sopenharmony_ci		fclose(fp);
247c72fcc34Sopenharmony_ci	}
248c72fcc34Sopenharmony_ci	return err;
249c72fcc34Sopenharmony_ci}
250c72fcc34Sopenharmony_ci
251c72fcc34Sopenharmony_cistatic int get_tiny_device(struct bat *bat, char *alsa_device,
252c72fcc34Sopenharmony_ci		unsigned int *tiny_card, unsigned int *tiny_device)
253c72fcc34Sopenharmony_ci{
254c72fcc34Sopenharmony_ci	char *tmp1, *tmp2, *tmp3;
255c72fcc34Sopenharmony_ci
256c72fcc34Sopenharmony_ci	if (alsa_device == NULL)
257c72fcc34Sopenharmony_ci		goto fail;
258c72fcc34Sopenharmony_ci
259c72fcc34Sopenharmony_ci	tmp1 = strchr(alsa_device, ':');
260c72fcc34Sopenharmony_ci	if (tmp1 == NULL)
261c72fcc34Sopenharmony_ci		goto fail;
262c72fcc34Sopenharmony_ci
263c72fcc34Sopenharmony_ci	tmp3 = tmp1 + 1;
264c72fcc34Sopenharmony_ci	tmp2 = strchr(tmp3, ',');
265c72fcc34Sopenharmony_ci	if (tmp2 == NULL)
266c72fcc34Sopenharmony_ci		goto fail;
267c72fcc34Sopenharmony_ci
268c72fcc34Sopenharmony_ci	tmp1 = tmp2 + 1;
269c72fcc34Sopenharmony_ci	*tiny_device = atoi(tmp1);
270c72fcc34Sopenharmony_ci	*tmp2 = '\0';
271c72fcc34Sopenharmony_ci	*tiny_card = atoi(tmp3);
272c72fcc34Sopenharmony_ci	*tmp2 = ',';
273c72fcc34Sopenharmony_ci
274c72fcc34Sopenharmony_ci	return 0;
275c72fcc34Sopenharmony_cifail:
276c72fcc34Sopenharmony_ci	fprintf(bat->err, _("Invalid tiny device: %s\n"), alsa_device);
277c72fcc34Sopenharmony_ci	return -EINVAL;
278c72fcc34Sopenharmony_ci}
279c72fcc34Sopenharmony_ci
280c72fcc34Sopenharmony_ci/**
281c72fcc34Sopenharmony_ci * Play
282c72fcc34Sopenharmony_ci */
283c72fcc34Sopenharmony_civoid *playback_tinyalsa(struct bat *bat)
284c72fcc34Sopenharmony_ci{
285c72fcc34Sopenharmony_ci	int err = 0;
286c72fcc34Sopenharmony_ci	struct pcm_config config;
287c72fcc34Sopenharmony_ci	struct pcm *pcm = NULL;
288c72fcc34Sopenharmony_ci	void *buffer = NULL;
289c72fcc34Sopenharmony_ci	int bufbytes;
290c72fcc34Sopenharmony_ci
291c72fcc34Sopenharmony_ci	fprintf(bat->log, _("Entering playback thread (tinyalsa).\n"));
292c72fcc34Sopenharmony_ci
293c72fcc34Sopenharmony_ci	retval_play = 0;
294c72fcc34Sopenharmony_ci
295c72fcc34Sopenharmony_ci	/* init device */
296c72fcc34Sopenharmony_ci	err = get_tiny_device(bat, bat->playback.device,
297c72fcc34Sopenharmony_ci			&bat->playback.card_tiny,
298c72fcc34Sopenharmony_ci			&bat->playback.device_tiny);
299c72fcc34Sopenharmony_ci	if (err < 0) {
300c72fcc34Sopenharmony_ci		retval_play = err;
301c72fcc34Sopenharmony_ci		goto exit1;
302c72fcc34Sopenharmony_ci	}
303c72fcc34Sopenharmony_ci
304c72fcc34Sopenharmony_ci	/* init config */
305c72fcc34Sopenharmony_ci	err = init_config(bat, &config);
306c72fcc34Sopenharmony_ci	if (err < 0) {
307c72fcc34Sopenharmony_ci		retval_play = err;
308c72fcc34Sopenharmony_ci		goto exit1;
309c72fcc34Sopenharmony_ci	}
310c72fcc34Sopenharmony_ci
311c72fcc34Sopenharmony_ci	/* check param before open device */
312c72fcc34Sopenharmony_ci	err = check_playback_params(bat, &config);
313c72fcc34Sopenharmony_ci	if (err < 0) {
314c72fcc34Sopenharmony_ci		retval_play = err;
315c72fcc34Sopenharmony_ci		goto exit1;
316c72fcc34Sopenharmony_ci	}
317c72fcc34Sopenharmony_ci
318c72fcc34Sopenharmony_ci	/* open device */
319c72fcc34Sopenharmony_ci	pcm = pcm_open(bat->playback.card_tiny, bat->playback.device_tiny,
320c72fcc34Sopenharmony_ci			PCM_OUT, &config);
321c72fcc34Sopenharmony_ci	if (!pcm || !pcm_is_ready(pcm)) {
322c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Unable to open PCM device %u (%s)!\n"),
323c72fcc34Sopenharmony_ci				bat->playback.device_tiny, pcm_get_error(pcm));
324c72fcc34Sopenharmony_ci		retval_play = -EINVAL;
325c72fcc34Sopenharmony_ci		goto exit1;
326c72fcc34Sopenharmony_ci	}
327c72fcc34Sopenharmony_ci
328c72fcc34Sopenharmony_ci	/* init buffer */
329c72fcc34Sopenharmony_ci	bufbytes = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
330c72fcc34Sopenharmony_ci	buffer = malloc(bufbytes);
331c72fcc34Sopenharmony_ci	if (!buffer) {
332c72fcc34Sopenharmony_ci		retval_play = -ENOMEM;
333c72fcc34Sopenharmony_ci		goto exit2;
334c72fcc34Sopenharmony_ci	}
335c72fcc34Sopenharmony_ci
336c72fcc34Sopenharmony_ci	/* init playback source */
337c72fcc34Sopenharmony_ci	if (bat->playback.file == NULL) {
338c72fcc34Sopenharmony_ci		fprintf(bat->log, _("Playing generated audio sine wave"));
339c72fcc34Sopenharmony_ci		bat->sinus_duration == 0 ?
340c72fcc34Sopenharmony_ci			fprintf(bat->log, _(" endlessly\n")) :
341c72fcc34Sopenharmony_ci			fprintf(bat->log, _("\n"));
342c72fcc34Sopenharmony_ci	} else {
343c72fcc34Sopenharmony_ci		fprintf(bat->log, _("Playing input audio file: %s\n"),
344c72fcc34Sopenharmony_ci				bat->playback.file);
345c72fcc34Sopenharmony_ci		bat->fp = fopen(bat->playback.file, "rb");
346c72fcc34Sopenharmony_ci		err = -errno;
347c72fcc34Sopenharmony_ci		if (bat->fp == NULL) {
348c72fcc34Sopenharmony_ci			fprintf(bat->err, _("Cannot open file: %s %d\n"),
349c72fcc34Sopenharmony_ci					bat->playback.file, err);
350c72fcc34Sopenharmony_ci			retval_play = err;
351c72fcc34Sopenharmony_ci			goto exit3;
352c72fcc34Sopenharmony_ci		}
353c72fcc34Sopenharmony_ci		/* Skip header */
354c72fcc34Sopenharmony_ci		err = read_wav_header(bat, bat->playback.file, bat->fp, true);
355c72fcc34Sopenharmony_ci		if (err != 0) {
356c72fcc34Sopenharmony_ci			retval_play = err;
357c72fcc34Sopenharmony_ci			goto exit4;
358c72fcc34Sopenharmony_ci		}
359c72fcc34Sopenharmony_ci	}
360c72fcc34Sopenharmony_ci
361c72fcc34Sopenharmony_ci	if (bat->roundtriplatency)
362c72fcc34Sopenharmony_ci		err = latencytest_process_output(bat, pcm, buffer, bufbytes);
363c72fcc34Sopenharmony_ci	else
364c72fcc34Sopenharmony_ci		err = play_sample(bat, pcm, buffer, bufbytes);
365c72fcc34Sopenharmony_ci	if (err < 0) {
366c72fcc34Sopenharmony_ci		retval_play = err;
367c72fcc34Sopenharmony_ci		goto exit4;
368c72fcc34Sopenharmony_ci	}
369c72fcc34Sopenharmony_ci
370c72fcc34Sopenharmony_ciexit4:
371c72fcc34Sopenharmony_ci	if (bat->playback.file)
372c72fcc34Sopenharmony_ci		fclose(bat->fp);
373c72fcc34Sopenharmony_ciexit3:
374c72fcc34Sopenharmony_ci	free(buffer);
375c72fcc34Sopenharmony_ciexit2:
376c72fcc34Sopenharmony_ci	pcm_close(pcm);
377c72fcc34Sopenharmony_ciexit1:
378c72fcc34Sopenharmony_ci	pthread_exit(&retval_play);
379c72fcc34Sopenharmony_ci}
380c72fcc34Sopenharmony_ci
381c72fcc34Sopenharmony_ci/**
382c72fcc34Sopenharmony_ci * Capture sample
383c72fcc34Sopenharmony_ci */
384c72fcc34Sopenharmony_cistatic int capture_sample(struct bat *bat, struct pcm *pcm,
385c72fcc34Sopenharmony_ci		void *buffer, unsigned int bytes)
386c72fcc34Sopenharmony_ci{
387c72fcc34Sopenharmony_ci	int err = 0;
388c72fcc34Sopenharmony_ci	FILE *fp = NULL;
389c72fcc34Sopenharmony_ci	unsigned int bytes_read = 0;
390c72fcc34Sopenharmony_ci	unsigned int bytes_count = bat->frames * bat->frame_size;
391c72fcc34Sopenharmony_ci
392c72fcc34Sopenharmony_ci	remove(bat->capture.file);
393c72fcc34Sopenharmony_ci	fp = fopen(bat->capture.file, "wb");
394c72fcc34Sopenharmony_ci	err = -errno;
395c72fcc34Sopenharmony_ci	if (fp == NULL) {
396c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Cannot open file: %s %d\n"),
397c72fcc34Sopenharmony_ci				bat->capture.file, err);
398c72fcc34Sopenharmony_ci		return err;
399c72fcc34Sopenharmony_ci	}
400c72fcc34Sopenharmony_ci	/* leave space for file header */
401c72fcc34Sopenharmony_ci	if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
402c72fcc34Sopenharmony_ci		err = -errno;
403c72fcc34Sopenharmony_ci		fclose(fp);
404c72fcc34Sopenharmony_ci		return err;
405c72fcc34Sopenharmony_ci	}
406c72fcc34Sopenharmony_ci
407c72fcc34Sopenharmony_ci	while (bytes_read < bytes_count && !pcm_read(pcm, buffer, bytes)) {
408c72fcc34Sopenharmony_ci		if (fwrite(buffer, 1, bytes, fp) != bytes)
409c72fcc34Sopenharmony_ci			break;
410c72fcc34Sopenharmony_ci
411c72fcc34Sopenharmony_ci		bytes_read += bytes;
412c72fcc34Sopenharmony_ci
413c72fcc34Sopenharmony_ci		bat->periods_played++;
414c72fcc34Sopenharmony_ci
415c72fcc34Sopenharmony_ci		if (bat->period_is_limited
416c72fcc34Sopenharmony_ci				&& bat->periods_played >= bat->periods_total)
417c72fcc34Sopenharmony_ci			break;
418c72fcc34Sopenharmony_ci	}
419c72fcc34Sopenharmony_ci
420c72fcc34Sopenharmony_ci	err = update_wav_header(bat, fp, bytes_read);
421c72fcc34Sopenharmony_ci
422c72fcc34Sopenharmony_ci	fclose(fp);
423c72fcc34Sopenharmony_ci	return err;
424c72fcc34Sopenharmony_ci}
425c72fcc34Sopenharmony_ci
426c72fcc34Sopenharmony_ci/**
427c72fcc34Sopenharmony_ci * Process input data for latency test
428c72fcc34Sopenharmony_ci */
429c72fcc34Sopenharmony_cistatic int latencytest_process_input(struct bat *bat, struct pcm *pcm,
430c72fcc34Sopenharmony_ci		void *buffer, unsigned int bytes)
431c72fcc34Sopenharmony_ci{
432c72fcc34Sopenharmony_ci	int err = 0;
433c72fcc34Sopenharmony_ci	FILE *fp = NULL;
434c72fcc34Sopenharmony_ci	unsigned int bytes_read = 0;
435c72fcc34Sopenharmony_ci	unsigned int bytes_count = bat->frames * bat->frame_size;
436c72fcc34Sopenharmony_ci
437c72fcc34Sopenharmony_ci	remove(bat->capture.file);
438c72fcc34Sopenharmony_ci	fp = fopen(bat->capture.file, "wb");
439c72fcc34Sopenharmony_ci	err = -errno;
440c72fcc34Sopenharmony_ci	if (fp == NULL) {
441c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Cannot open file: %s %d\n"),
442c72fcc34Sopenharmony_ci				bat->capture.file, err);
443c72fcc34Sopenharmony_ci		return err;
444c72fcc34Sopenharmony_ci	}
445c72fcc34Sopenharmony_ci	/* leave space for file header */
446c72fcc34Sopenharmony_ci	if (fseek(fp, sizeof(struct wav_container), SEEK_SET) != 0) {
447c72fcc34Sopenharmony_ci		err = -errno;
448c72fcc34Sopenharmony_ci		fclose(fp);
449c72fcc34Sopenharmony_ci		return err;
450c72fcc34Sopenharmony_ci	}
451c72fcc34Sopenharmony_ci
452c72fcc34Sopenharmony_ci	bat->latency.is_capturing = true;
453c72fcc34Sopenharmony_ci
454c72fcc34Sopenharmony_ci	while (bytes_read < bytes_count && !pcm_read(pcm, buffer, bytes)) {
455c72fcc34Sopenharmony_ci		if (fwrite(buffer, 1, bytes, fp) != bytes)
456c72fcc34Sopenharmony_ci			break;
457c72fcc34Sopenharmony_ci
458c72fcc34Sopenharmony_ci		err = handleinput(bat, buffer, bytes / bat->frame_size);
459c72fcc34Sopenharmony_ci		if (err != 0)
460c72fcc34Sopenharmony_ci			break;
461c72fcc34Sopenharmony_ci
462c72fcc34Sopenharmony_ci		if (bat->latency.is_playing == false)
463c72fcc34Sopenharmony_ci			break;
464c72fcc34Sopenharmony_ci
465c72fcc34Sopenharmony_ci		bytes_read += bytes;
466c72fcc34Sopenharmony_ci	}
467c72fcc34Sopenharmony_ci
468c72fcc34Sopenharmony_ci	bat->latency.is_capturing = false;
469c72fcc34Sopenharmony_ci
470c72fcc34Sopenharmony_ci	err = update_wav_header(bat, fp, bytes_read);
471c72fcc34Sopenharmony_ci
472c72fcc34Sopenharmony_ci	fclose(fp);
473c72fcc34Sopenharmony_ci	return err;
474c72fcc34Sopenharmony_ci}
475c72fcc34Sopenharmony_ci
476c72fcc34Sopenharmony_ci/**
477c72fcc34Sopenharmony_ci * Record
478c72fcc34Sopenharmony_ci */
479c72fcc34Sopenharmony_civoid *record_tinyalsa(struct bat *bat)
480c72fcc34Sopenharmony_ci{
481c72fcc34Sopenharmony_ci	int err = 0;
482c72fcc34Sopenharmony_ci	struct pcm_config config;
483c72fcc34Sopenharmony_ci	struct pcm *pcm;
484c72fcc34Sopenharmony_ci	void *buffer;
485c72fcc34Sopenharmony_ci	unsigned int bufbytes;
486c72fcc34Sopenharmony_ci
487c72fcc34Sopenharmony_ci	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
488c72fcc34Sopenharmony_ci
489c72fcc34Sopenharmony_ci	fprintf(bat->log, _("Entering capture thread (tinyalsa).\n"));
490c72fcc34Sopenharmony_ci
491c72fcc34Sopenharmony_ci	retval_record = 0;
492c72fcc34Sopenharmony_ci
493c72fcc34Sopenharmony_ci	/* init device */
494c72fcc34Sopenharmony_ci	err = get_tiny_device(bat, bat->capture.device,
495c72fcc34Sopenharmony_ci			&bat->capture.card_tiny,
496c72fcc34Sopenharmony_ci			&bat->capture.device_tiny);
497c72fcc34Sopenharmony_ci	if (err < 0) {
498c72fcc34Sopenharmony_ci		retval_record = err;
499c72fcc34Sopenharmony_ci		goto exit1;
500c72fcc34Sopenharmony_ci	}
501c72fcc34Sopenharmony_ci
502c72fcc34Sopenharmony_ci	/* init config */
503c72fcc34Sopenharmony_ci	err = init_config(bat, &config);
504c72fcc34Sopenharmony_ci	if (err < 0) {
505c72fcc34Sopenharmony_ci		retval_record = err;
506c72fcc34Sopenharmony_ci		goto exit1;
507c72fcc34Sopenharmony_ci	}
508c72fcc34Sopenharmony_ci
509c72fcc34Sopenharmony_ci	/* open device */
510c72fcc34Sopenharmony_ci	pcm = pcm_open(bat->capture.card_tiny, bat->capture.device_tiny,
511c72fcc34Sopenharmony_ci			PCM_IN, &config);
512c72fcc34Sopenharmony_ci	if (!pcm || !pcm_is_ready(pcm)) {
513c72fcc34Sopenharmony_ci		fprintf(bat->err, _("Unable to open PCM device (%s)!\n"),
514c72fcc34Sopenharmony_ci				pcm_get_error(pcm));
515c72fcc34Sopenharmony_ci		retval_record = -EINVAL;
516c72fcc34Sopenharmony_ci		goto exit1;
517c72fcc34Sopenharmony_ci	}
518c72fcc34Sopenharmony_ci
519c72fcc34Sopenharmony_ci	/* init buffer */
520c72fcc34Sopenharmony_ci	bufbytes = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm));
521c72fcc34Sopenharmony_ci	buffer = malloc(bufbytes);
522c72fcc34Sopenharmony_ci	if (!buffer) {
523c72fcc34Sopenharmony_ci		retval_record = -ENOMEM;
524c72fcc34Sopenharmony_ci		goto exit2;
525c72fcc34Sopenharmony_ci	}
526c72fcc34Sopenharmony_ci
527c72fcc34Sopenharmony_ci	pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
528c72fcc34Sopenharmony_ci	pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
529c72fcc34Sopenharmony_ci	pthread_cleanup_push(close_handle, pcm);
530c72fcc34Sopenharmony_ci	pthread_cleanup_push(free, buffer);
531c72fcc34Sopenharmony_ci
532c72fcc34Sopenharmony_ci	fprintf(bat->log, _("Recording ...\n"));
533c72fcc34Sopenharmony_ci	if (bat->roundtriplatency)
534c72fcc34Sopenharmony_ci		err = latencytest_process_input(bat, pcm, buffer, bufbytes);
535c72fcc34Sopenharmony_ci	else
536c72fcc34Sopenharmony_ci		err = capture_sample(bat, pcm, buffer, bufbytes);
537c72fcc34Sopenharmony_ci	if (err != 0) {
538c72fcc34Sopenharmony_ci		retval_record = err;
539c72fcc34Sopenharmony_ci		goto exit3;
540c72fcc34Sopenharmony_ci	}
541c72fcc34Sopenharmony_ci
542c72fcc34Sopenharmony_ci	/* Normally we will never reach this part of code (unless error in
543c72fcc34Sopenharmony_ci	 * previous call) (before exit3) as this thread will be cancelled
544c72fcc34Sopenharmony_ci	 *  by end of play thread. Except in single line mode. */
545c72fcc34Sopenharmony_ci	pthread_cleanup_pop(0);
546c72fcc34Sopenharmony_ci	pthread_cleanup_pop(0);
547c72fcc34Sopenharmony_ci	pthread_exit(&retval_record);
548c72fcc34Sopenharmony_ci
549c72fcc34Sopenharmony_ciexit3:
550c72fcc34Sopenharmony_ci	free(buffer);
551c72fcc34Sopenharmony_ciexit2:
552c72fcc34Sopenharmony_ci	pcm_close(pcm);
553c72fcc34Sopenharmony_ciexit1:
554c72fcc34Sopenharmony_ci	pthread_exit(&retval_record);
555c72fcc34Sopenharmony_ci}
556