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