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