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