latencytest.c (6308B)
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 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 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 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 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->format = BAT_PCM_FORMAT_S16_LE; 182 bat->frames = LATENCY_TEST_TIME_LIMIT * bat->rate; 183 bat->periods_played = 0; 184 } 185 186 int handleinput(struct bat *bat, void *buffer, int frames) 187 { 188 switch (bat->latency.state) { 189 /* Measuring average loudness for 1 second */ 190 case LATENCY_STATE_MEASURE_FOR_1_SECOND: 191 bat->latency.sum += sumaudio(bat, buffer, frames); 192 bat->latency.samples += frames; 193 194 /* 1 second elapsed */ 195 if (bat->latency.samples >= bat->rate) { 196 calculate_threshold(bat); 197 bat->latency.state = LATENCY_STATE_PLAY_AND_LISTEN; 198 bat->latency.samples = 0; 199 bat->latency.sum = 0; 200 } 201 break; 202 203 /* Playing sine wave and listening if it comes back */ 204 case LATENCY_STATE_PLAY_AND_LISTEN: 205 play_and_listen(bat, buffer, frames); 206 break; 207 208 /* Waiting 1 second */ 209 case LATENCY_STATE_WAITING: 210 bat->latency.samples += frames; 211 212 if (bat->latency.samples > bat->rate) { 213 /* 1 second elapsed, start over */ 214 bat->latency.samples = 0; 215 bat->latency.state = LATENCY_STATE_MEASURE_FOR_1_SECOND; 216 } 217 break; 218 219 default: 220 return 0; 221 } 222 223 return 0; 224 } 225 226 int handleoutput(struct bat *bat, void *buffer, int bytes, int frames) 227 { 228 int err = 0; 229 230 /* If capture completed, terminate the playback */ 231 if (bat->periods_played * frames > 2 * bat->rate 232 && bat->latency.is_capturing == false) 233 return bat->latency.state; 234 235 if (bat->latency.state == LATENCY_STATE_PLAY_AND_LISTEN) 236 err = generate_sine_wave(bat, frames, buffer); 237 else 238 /* Output silence */ 239 memset(buffer, 0, bytes); 240 241 return err; 242 }