tarina

git clone https://git.tarina.org/tarina
Log | Files | Refs | README | LICENSE

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 }