xref: /third_party/alsa-utils/bat/latencytest.c (revision c72fcc34)
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 <math.h>
19c72fcc34Sopenharmony_ci#include <stdio.h>
20c72fcc34Sopenharmony_ci#include <stdlib.h>
21c72fcc34Sopenharmony_ci#include <string.h>
22c72fcc34Sopenharmony_ci#include <stdbool.h>
23c72fcc34Sopenharmony_ci
24c72fcc34Sopenharmony_ci#include "common.h"
25c72fcc34Sopenharmony_ci#include "bat-signal.h"
26c72fcc34Sopenharmony_ci#include "gettext.h"
27c72fcc34Sopenharmony_ci
28c72fcc34Sopenharmony_ci/* How one measurement step works:
29c72fcc34Sopenharmony_ci   - Listen and measure the average loudness of the environment for 1 second.
30c72fcc34Sopenharmony_ci   - Create a threshold value 16 decibels higher than the average loudness.
31c72fcc34Sopenharmony_ci   - Begin playing a ~1000 Hz sine wave and start counting the samples elapsed.
32c72fcc34Sopenharmony_ci   - Stop counting and playing if the input's loudness is higher than the
33c72fcc34Sopenharmony_ci     threshold, as the output wave is probably coming back.
34c72fcc34Sopenharmony_ci   - Calculate the round trip audio latency value in milliseconds. */
35c72fcc34Sopenharmony_ci
36c72fcc34Sopenharmony_cistatic float sumaudio(struct bat *bat, short int *buffer, int frames)
37c72fcc34Sopenharmony_ci{
38c72fcc34Sopenharmony_ci	float sum = 0;
39c72fcc34Sopenharmony_ci	int n = 0;
40c72fcc34Sopenharmony_ci
41c72fcc34Sopenharmony_ci	while (frames) {
42c72fcc34Sopenharmony_ci		frames--;
43c72fcc34Sopenharmony_ci
44c72fcc34Sopenharmony_ci		for (n = 0; n < bat->channels; n++) {
45c72fcc34Sopenharmony_ci			sum += abs(buffer[0]);
46c72fcc34Sopenharmony_ci			buffer++;
47c72fcc34Sopenharmony_ci		}
48c72fcc34Sopenharmony_ci	}
49c72fcc34Sopenharmony_ci
50c72fcc34Sopenharmony_ci	sum = sum / bat->channels;
51c72fcc34Sopenharmony_ci
52c72fcc34Sopenharmony_ci	return sum;
53c72fcc34Sopenharmony_ci}
54c72fcc34Sopenharmony_ci
55c72fcc34Sopenharmony_cistatic void play_and_listen(struct bat *bat, void *buffer, int frames)
56c72fcc34Sopenharmony_ci{
57c72fcc34Sopenharmony_ci	int averageinput;
58c72fcc34Sopenharmony_ci	int n = 0;
59c72fcc34Sopenharmony_ci	float sum = 0;
60c72fcc34Sopenharmony_ci	float max = 0;
61c72fcc34Sopenharmony_ci	float min = 100000.0f;
62c72fcc34Sopenharmony_ci	short int *input;
63c72fcc34Sopenharmony_ci	int num = bat->latency.number;
64c72fcc34Sopenharmony_ci
65c72fcc34Sopenharmony_ci	averageinput = (int) (sumaudio(bat, buffer, frames) / frames);
66c72fcc34Sopenharmony_ci
67c72fcc34Sopenharmony_ci	/* The signal is above threshold
68c72fcc34Sopenharmony_ci	   So our sine wave comes back on the input */
69c72fcc34Sopenharmony_ci	if (averageinput > bat->latency.threshold) {
70c72fcc34Sopenharmony_ci		input = buffer;
71c72fcc34Sopenharmony_ci
72c72fcc34Sopenharmony_ci		/* Check the location when it became loud enough */
73c72fcc34Sopenharmony_ci		while (n < frames) {
74c72fcc34Sopenharmony_ci			if (*input++ > bat->latency.threshold)
75c72fcc34Sopenharmony_ci				break;
76c72fcc34Sopenharmony_ci			*input += bat->channels;
77c72fcc34Sopenharmony_ci			n++;
78c72fcc34Sopenharmony_ci		}
79c72fcc34Sopenharmony_ci
80c72fcc34Sopenharmony_ci		/* Now we get the total round trip latency*/
81c72fcc34Sopenharmony_ci		bat->latency.samples += n;
82c72fcc34Sopenharmony_ci
83c72fcc34Sopenharmony_ci		/* Expect at least 1 buffer of round trip latency. */
84c72fcc34Sopenharmony_ci		if (bat->latency.samples > frames) {
85c72fcc34Sopenharmony_ci			bat->latency.result[num - 1] =
86c72fcc34Sopenharmony_ci				(float) bat->latency.samples * 1000 / bat->rate;
87c72fcc34Sopenharmony_ci			fprintf(bat->log,
88c72fcc34Sopenharmony_ci					 _("Test%d, round trip latency %dms\n"),
89c72fcc34Sopenharmony_ci					num,
90c72fcc34Sopenharmony_ci					(int) bat->latency.result[num - 1]);
91c72fcc34Sopenharmony_ci
92c72fcc34Sopenharmony_ci			for (n = 0; n < num; n++) {
93c72fcc34Sopenharmony_ci				if (bat->latency.result[n] > max)
94c72fcc34Sopenharmony_ci					max = bat->latency.result[n];
95c72fcc34Sopenharmony_ci				if (bat->latency.result[n] < min)
96c72fcc34Sopenharmony_ci					min = bat->latency.result[n];
97c72fcc34Sopenharmony_ci				sum += bat->latency.result[n];
98c72fcc34Sopenharmony_ci			}
99c72fcc34Sopenharmony_ci
100c72fcc34Sopenharmony_ci			/* The maximum is higher than the minimum's double */
101c72fcc34Sopenharmony_ci			if (max / min > 2.0f) {
102c72fcc34Sopenharmony_ci				bat->latency.state =
103c72fcc34Sopenharmony_ci					LATENCY_STATE_COMPLETE_FAILURE;
104c72fcc34Sopenharmony_ci				bat->latency.is_capturing = false;
105c72fcc34Sopenharmony_ci				return;
106c72fcc34Sopenharmony_ci
107c72fcc34Sopenharmony_ci			/* Final results */
108c72fcc34Sopenharmony_ci			} else if (num == LATENCY_TEST_NUMBER) {
109c72fcc34Sopenharmony_ci				bat->latency.final_result =
110c72fcc34Sopenharmony_ci					(int) (sum / LATENCY_TEST_NUMBER);
111c72fcc34Sopenharmony_ci				fprintf(bat->log,
112c72fcc34Sopenharmony_ci					_("Final round trip latency: %dms\n"),
113c72fcc34Sopenharmony_ci					bat->latency.final_result);
114c72fcc34Sopenharmony_ci
115c72fcc34Sopenharmony_ci				bat->latency.state =
116c72fcc34Sopenharmony_ci					LATENCY_STATE_COMPLETE_SUCCESS;
117c72fcc34Sopenharmony_ci				bat->latency.is_capturing = false;
118c72fcc34Sopenharmony_ci				return;
119c72fcc34Sopenharmony_ci
120c72fcc34Sopenharmony_ci			/* Next step */
121c72fcc34Sopenharmony_ci			} else
122c72fcc34Sopenharmony_ci				bat->latency.state = LATENCY_STATE_WAITING;
123c72fcc34Sopenharmony_ci
124c72fcc34Sopenharmony_ci			bat->latency.number++;
125c72fcc34Sopenharmony_ci
126c72fcc34Sopenharmony_ci		} else
127c72fcc34Sopenharmony_ci			/* Happens when an early noise comes in */
128c72fcc34Sopenharmony_ci			bat->latency.state = LATENCY_STATE_WAITING;
129c72fcc34Sopenharmony_ci
130c72fcc34Sopenharmony_ci	} else {
131c72fcc34Sopenharmony_ci		/* Still listening */
132c72fcc34Sopenharmony_ci		bat->latency.samples += frames;
133c72fcc34Sopenharmony_ci
134c72fcc34Sopenharmony_ci		/* Do not listen to more than a second
135c72fcc34Sopenharmony_ci		   Maybe too much background noise */
136c72fcc34Sopenharmony_ci		if ((unsigned int)bat->latency.samples > bat->rate) {
137c72fcc34Sopenharmony_ci			bat->latency.error++;
138c72fcc34Sopenharmony_ci
139c72fcc34Sopenharmony_ci			if (bat->latency.error > LATENCY_TEST_NUMBER) {
140c72fcc34Sopenharmony_ci				fprintf(bat->err,
141c72fcc34Sopenharmony_ci					_("Could not detect signal."));
142c72fcc34Sopenharmony_ci				fprintf(bat->err,
143c72fcc34Sopenharmony_ci					_("Too much background noise?\n"));
144c72fcc34Sopenharmony_ci				bat->latency.state =
145c72fcc34Sopenharmony_ci					LATENCY_STATE_COMPLETE_FAILURE;
146c72fcc34Sopenharmony_ci				bat->latency.is_capturing = false;
147c72fcc34Sopenharmony_ci				return;
148c72fcc34Sopenharmony_ci			}
149c72fcc34Sopenharmony_ci
150c72fcc34Sopenharmony_ci			/* let's start over */
151c72fcc34Sopenharmony_ci			bat->latency.state = LATENCY_STATE_WAITING;
152c72fcc34Sopenharmony_ci		}
153c72fcc34Sopenharmony_ci	}
154c72fcc34Sopenharmony_ci
155c72fcc34Sopenharmony_ci	return;
156c72fcc34Sopenharmony_ci}
157c72fcc34Sopenharmony_ci
158c72fcc34Sopenharmony_cistatic void calculate_threshold(struct bat *bat)
159c72fcc34Sopenharmony_ci{
160c72fcc34Sopenharmony_ci	float average;
161c72fcc34Sopenharmony_ci	float reference;
162c72fcc34Sopenharmony_ci
163c72fcc34Sopenharmony_ci	/* Calculate the average loudness of the environment and create
164c72fcc34Sopenharmony_ci	   a threshold value 16 decibels higher than the average loudness */
165c72fcc34Sopenharmony_ci	average = bat->latency.sum / bat->latency.samples / 32767.0f;
166c72fcc34Sopenharmony_ci	reference = 20.0f * log10f(average) + 16.0f;
167c72fcc34Sopenharmony_ci	bat->latency.threshold = (int) (powf(10.0f, reference / 20.0f)
168c72fcc34Sopenharmony_ci						* 32767.0f);
169c72fcc34Sopenharmony_ci}
170c72fcc34Sopenharmony_ci
171c72fcc34Sopenharmony_civoid roundtrip_latency_init(struct bat *bat)
172c72fcc34Sopenharmony_ci{
173c72fcc34Sopenharmony_ci	bat->latency.number = 1;
174c72fcc34Sopenharmony_ci	bat->latency.state = LATENCY_STATE_MEASURE_FOR_1_SECOND;
175c72fcc34Sopenharmony_ci	bat->latency.final_result = 0;
176c72fcc34Sopenharmony_ci	bat->latency.samples = 0;
177c72fcc34Sopenharmony_ci	bat->latency.sum = 0;
178c72fcc34Sopenharmony_ci	bat->latency.threshold = 0;
179c72fcc34Sopenharmony_ci	bat->latency.is_capturing = false;
180c72fcc34Sopenharmony_ci	bat->latency.is_playing = false;
181c72fcc34Sopenharmony_ci	bat->latency.error = 0;
182c72fcc34Sopenharmony_ci	bat->latency.xrun_error = false;
183c72fcc34Sopenharmony_ci	bat->frames = LATENCY_TEST_TIME_LIMIT * bat->rate;
184c72fcc34Sopenharmony_ci	bat->periods_played = 0;
185c72fcc34Sopenharmony_ci}
186c72fcc34Sopenharmony_ci
187c72fcc34Sopenharmony_ciint handleinput(struct bat *bat, void *buffer, int frames)
188c72fcc34Sopenharmony_ci{
189c72fcc34Sopenharmony_ci	switch (bat->latency.state) {
190c72fcc34Sopenharmony_ci	/* Measuring average loudness for 1 second */
191c72fcc34Sopenharmony_ci	case LATENCY_STATE_MEASURE_FOR_1_SECOND:
192c72fcc34Sopenharmony_ci		bat->latency.sum += sumaudio(bat, buffer, frames);
193c72fcc34Sopenharmony_ci		bat->latency.samples += frames;
194c72fcc34Sopenharmony_ci
195c72fcc34Sopenharmony_ci		/* 1 second elapsed */
196c72fcc34Sopenharmony_ci		if ((unsigned int)bat->latency.samples >= bat->rate) {
197c72fcc34Sopenharmony_ci			calculate_threshold(bat);
198c72fcc34Sopenharmony_ci			bat->latency.state = LATENCY_STATE_PLAY_AND_LISTEN;
199c72fcc34Sopenharmony_ci			bat->latency.samples = 0;
200c72fcc34Sopenharmony_ci			bat->latency.sum = 0;
201c72fcc34Sopenharmony_ci		}
202c72fcc34Sopenharmony_ci		break;
203c72fcc34Sopenharmony_ci
204c72fcc34Sopenharmony_ci	/* Playing sine wave and listening if it comes back */
205c72fcc34Sopenharmony_ci	case LATENCY_STATE_PLAY_AND_LISTEN:
206c72fcc34Sopenharmony_ci		play_and_listen(bat, buffer, frames);
207c72fcc34Sopenharmony_ci		break;
208c72fcc34Sopenharmony_ci
209c72fcc34Sopenharmony_ci	/* Waiting 1 second */
210c72fcc34Sopenharmony_ci	case LATENCY_STATE_WAITING:
211c72fcc34Sopenharmony_ci		bat->latency.samples += frames;
212c72fcc34Sopenharmony_ci
213c72fcc34Sopenharmony_ci		if ((unsigned int)bat->latency.samples > bat->rate) {
214c72fcc34Sopenharmony_ci			/* 1 second elapsed, start over */
215c72fcc34Sopenharmony_ci			bat->latency.samples = 0;
216c72fcc34Sopenharmony_ci			bat->latency.state = LATENCY_STATE_MEASURE_FOR_1_SECOND;
217c72fcc34Sopenharmony_ci		}
218c72fcc34Sopenharmony_ci		break;
219c72fcc34Sopenharmony_ci
220c72fcc34Sopenharmony_ci	default:
221c72fcc34Sopenharmony_ci		return 0;
222c72fcc34Sopenharmony_ci	}
223c72fcc34Sopenharmony_ci
224c72fcc34Sopenharmony_ci	return 0;
225c72fcc34Sopenharmony_ci}
226c72fcc34Sopenharmony_ci
227c72fcc34Sopenharmony_ciint handleoutput(struct bat *bat, void *buffer, int bytes, int frames)
228c72fcc34Sopenharmony_ci{
229c72fcc34Sopenharmony_ci	int err = 0;
230c72fcc34Sopenharmony_ci
231c72fcc34Sopenharmony_ci	/* If capture completed, terminate the playback */
232c72fcc34Sopenharmony_ci	if (bat->periods_played * frames > 2 * bat->rate
233c72fcc34Sopenharmony_ci			&& bat->latency.is_capturing == false)
234c72fcc34Sopenharmony_ci		return bat->latency.state;
235c72fcc34Sopenharmony_ci
236c72fcc34Sopenharmony_ci	if (bat->latency.state == LATENCY_STATE_PLAY_AND_LISTEN)
237c72fcc34Sopenharmony_ci		err = generate_sine_wave(bat, frames, buffer);
238c72fcc34Sopenharmony_ci	else
239c72fcc34Sopenharmony_ci		/* Output silence */
240c72fcc34Sopenharmony_ci		memset(buffer, 0, bytes);
241c72fcc34Sopenharmony_ci
242c72fcc34Sopenharmony_ci	return err;
243c72fcc34Sopenharmony_ci}
244