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