Blame bat/latencytest.c

Packit Service a9274b
/*
Packit Service a9274b
 * Copyright (C) 2013-2015 Intel Corporation
Packit Service a9274b
 *
Packit Service a9274b
 * This program is free software; you can redistribute it and/or modify
Packit Service a9274b
 * it under the terms of the GNU General Public License as published by
Packit Service a9274b
 * the Free Software Foundation; either version 2 of the License, or
Packit Service a9274b
 * (at your option) any later version.
Packit Service a9274b
 *
Packit Service a9274b
 * This program is distributed in the hope that it will be useful,
Packit Service a9274b
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a9274b
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a9274b
 * GNU General Public License for more details.
Packit Service a9274b
 *
Packit Service a9274b
 */
Packit Service a9274b
Packit Service a9274b
#include <math.h>
Packit Service a9274b
#include <stdio.h>
Packit Service a9274b
#include <stdlib.h>
Packit Service a9274b
#include <string.h>
Packit Service a9274b
#include <stdbool.h>
Packit Service a9274b
Packit Service a9274b
#include "common.h"
Packit Service a9274b
#include "bat-signal.h"
Packit Service a9274b
#include "gettext.h"
Packit Service a9274b
Packit Service a9274b
/* How one measurement step works:
Packit Service a9274b
   - Listen and measure the average loudness of the environment for 1 second.
Packit Service a9274b
   - Create a threshold value 16 decibels higher than the average loudness.
Packit Service a9274b
   - Begin playing a ~1000 Hz sine wave and start counting the samples elapsed.
Packit Service a9274b
   - Stop counting and playing if the input's loudness is higher than the
Packit Service a9274b
     threshold, as the output wave is probably coming back.
Packit Service a9274b
   - Calculate the round trip audio latency value in milliseconds. */
Packit Service a9274b
Packit Service a9274b
static float sumaudio(struct bat *bat, short int *buffer, int frames)
Packit Service a9274b
{
Packit Service a9274b
	float sum = 0;
Packit Service a9274b
	int n = 0;
Packit Service a9274b
Packit Service a9274b
	while (frames) {
Packit Service a9274b
		frames--;
Packit Service a9274b
Packit Service a9274b
		for (n = 0; n < bat->channels; n++) {
Packit Service a9274b
			sum += abs(buffer[0]);
Packit Service a9274b
			buffer++;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	sum = sum / bat->channels;
Packit Service a9274b
Packit Service a9274b
	return sum;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void play_and_listen(struct bat *bat, void *buffer, int frames)
Packit Service a9274b
{
Packit Service a9274b
	int averageinput;
Packit Service a9274b
	int n = 0;
Packit Service a9274b
	float sum = 0;
Packit Service a9274b
	float max = 0;
Packit Service a9274b
	float min = 100000.0f;
Packit Service a9274b
	short int *input;
Packit Service a9274b
	int num = bat->latency.number;
Packit Service a9274b
Packit Service a9274b
	averageinput = (int) (sumaudio(bat, buffer, frames) / frames);
Packit Service a9274b
Packit Service a9274b
	/* The signal is above threshold
Packit Service a9274b
	   So our sine wave comes back on the input */
Packit Service a9274b
	if (averageinput > bat->latency.threshold) {
Packit Service a9274b
		input = buffer;
Packit Service a9274b
Packit Service a9274b
		/* Check the location when it became loud enough */
Packit Service a9274b
		while (n < frames) {
Packit Service a9274b
			if (*input++ > bat->latency.threshold)
Packit Service a9274b
				break;
Packit Service a9274b
			*input += bat->channels;
Packit Service a9274b
			n++;
Packit Service a9274b
		}
Packit Service a9274b
Packit Service a9274b
		/* Now we get the total round trip latency*/
Packit Service a9274b
		bat->latency.samples += n;
Packit Service a9274b
Packit Service a9274b
		/* Expect at least 1 buffer of round trip latency. */
Packit Service a9274b
		if (bat->latency.samples > frames) {
Packit Service a9274b
			bat->latency.result[num - 1] =
Packit Service a9274b
				(float) bat->latency.samples * 1000 / bat->rate;
Packit Service a9274b
			fprintf(bat->log,
Packit Service a9274b
					 _("Test%d, round trip latency %dms\n"),
Packit Service a9274b
					num,
Packit Service a9274b
					(int) bat->latency.result[num - 1]);
Packit Service a9274b
Packit Service a9274b
			for (n = 0; n < num; n++) {
Packit Service a9274b
				if (bat->latency.result[n] > max)
Packit Service a9274b
					max = bat->latency.result[n];
Packit Service a9274b
				if (bat->latency.result[n] < min)
Packit Service a9274b
					min = bat->latency.result[n];
Packit Service a9274b
				sum += bat->latency.result[n];
Packit Service a9274b
			}
Packit Service a9274b
Packit Service a9274b
			/* The maximum is higher than the minimum's double */
Packit Service a9274b
			if (max / min > 2.0f) {
Packit Service a9274b
				bat->latency.state =
Packit Service a9274b
					LATENCY_STATE_COMPLETE_FAILURE;
Packit Service a9274b
				bat->latency.is_capturing = false;
Packit Service a9274b
				return;
Packit Service a9274b
Packit Service a9274b
			/* Final results */
Packit Service a9274b
			} else if (num == LATENCY_TEST_NUMBER) {
Packit Service a9274b
				bat->latency.final_result =
Packit Service a9274b
					(int) (sum / LATENCY_TEST_NUMBER);
Packit Service a9274b
				fprintf(bat->log,
Packit Service a9274b
					_("Final round trip latency: %dms\n"),
Packit Service a9274b
					bat->latency.final_result);
Packit Service a9274b
Packit Service a9274b
				bat->latency.state =
Packit Service a9274b
					LATENCY_STATE_COMPLETE_SUCCESS;
Packit Service a9274b
				bat->latency.is_capturing = false;
Packit Service a9274b
				return;
Packit Service a9274b
Packit Service a9274b
			/* Next step */
Packit Service a9274b
			} else
Packit Service a9274b
				bat->latency.state = LATENCY_STATE_WAITING;
Packit Service a9274b
Packit Service a9274b
			bat->latency.number++;
Packit Service a9274b
Packit Service a9274b
		} else
Packit Service a9274b
			/* Happens when an early noise comes in */
Packit Service a9274b
			bat->latency.state = LATENCY_STATE_WAITING;
Packit Service a9274b
Packit Service a9274b
	} else {
Packit Service a9274b
		/* Still listening */
Packit Service a9274b
		bat->latency.samples += frames;
Packit Service a9274b
Packit Service a9274b
		/* Do not listen to more than a second
Packit Service a9274b
		   Maybe too much background noise */
Packit Service a9274b
		if (bat->latency.samples > bat->rate) {
Packit Service a9274b
			bat->latency.error++;
Packit Service a9274b
Packit Service a9274b
			if (bat->latency.error > LATENCY_TEST_NUMBER) {
Packit Service a9274b
				fprintf(bat->err,
Packit Service a9274b
					_("Could not detect signal."));
Packit Service a9274b
				fprintf(bat->err,
Packit Service a9274b
					_("Too much background noise?\n"));
Packit Service a9274b
				bat->latency.state =
Packit Service a9274b
					LATENCY_STATE_COMPLETE_FAILURE;
Packit Service a9274b
				bat->latency.is_capturing = false;
Packit Service a9274b
				return;
Packit Service a9274b
			}
Packit Service a9274b
Packit Service a9274b
			/* let's start over */
Packit Service a9274b
			bat->latency.state = LATENCY_STATE_WAITING;
Packit Service a9274b
		}
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
static void calculate_threshold(struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	float average;
Packit Service a9274b
	float reference;
Packit Service a9274b
Packit Service a9274b
	/* Calculate the average loudness of the environment and create
Packit Service a9274b
	   a threshold value 16 decibels higher than the average loudness */
Packit Service a9274b
	average = bat->latency.sum / bat->latency.samples / 32767.0f;
Packit Service a9274b
	reference = 20.0f * log10f(average) + 16.0f;
Packit Service a9274b
	bat->latency.threshold = (int) (powf(10.0f, reference / 20.0f)
Packit Service a9274b
						* 32767.0f);
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
void roundtrip_latency_init(struct bat *bat)
Packit Service a9274b
{
Packit Service a9274b
	bat->latency.number = 1;
Packit Service a9274b
	bat->latency.state = LATENCY_STATE_MEASURE_FOR_1_SECOND;
Packit Service a9274b
	bat->latency.final_result = 0;
Packit Service a9274b
	bat->latency.samples = 0;
Packit Service a9274b
	bat->latency.sum = 0;
Packit Service a9274b
	bat->latency.threshold = 0;
Packit Service a9274b
	bat->latency.is_capturing = false;
Packit Service a9274b
	bat->latency.is_playing = false;
Packit Service a9274b
	bat->latency.error = 0;
Packit Service a9274b
	bat->latency.xrun_error = false;
Packit Service a9274b
	bat->frames = LATENCY_TEST_TIME_LIMIT * bat->rate;
Packit Service a9274b
	bat->periods_played = 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int handleinput(struct bat *bat, void *buffer, int frames)
Packit Service a9274b
{
Packit Service a9274b
	switch (bat->latency.state) {
Packit Service a9274b
	/* Measuring average loudness for 1 second */
Packit Service a9274b
	case LATENCY_STATE_MEASURE_FOR_1_SECOND:
Packit Service a9274b
		bat->latency.sum += sumaudio(bat, buffer, frames);
Packit Service a9274b
		bat->latency.samples += frames;
Packit Service a9274b
Packit Service a9274b
		/* 1 second elapsed */
Packit Service a9274b
		if (bat->latency.samples >= bat->rate) {
Packit Service a9274b
			calculate_threshold(bat);
Packit Service a9274b
			bat->latency.state = LATENCY_STATE_PLAY_AND_LISTEN;
Packit Service a9274b
			bat->latency.samples = 0;
Packit Service a9274b
			bat->latency.sum = 0;
Packit Service a9274b
		}
Packit Service a9274b
		break;
Packit Service a9274b
Packit Service a9274b
	/* Playing sine wave and listening if it comes back */
Packit Service a9274b
	case LATENCY_STATE_PLAY_AND_LISTEN:
Packit Service a9274b
		play_and_listen(bat, buffer, frames);
Packit Service a9274b
		break;
Packit Service a9274b
Packit Service a9274b
	/* Waiting 1 second */
Packit Service a9274b
	case LATENCY_STATE_WAITING:
Packit Service a9274b
		bat->latency.samples += frames;
Packit Service a9274b
Packit Service a9274b
		if (bat->latency.samples > bat->rate) {
Packit Service a9274b
			/* 1 second elapsed, start over */
Packit Service a9274b
			bat->latency.samples = 0;
Packit Service a9274b
			bat->latency.state = LATENCY_STATE_MEASURE_FOR_1_SECOND;
Packit Service a9274b
		}
Packit Service a9274b
		break;
Packit Service a9274b
Packit Service a9274b
	default:
Packit Service a9274b
		return 0;
Packit Service a9274b
	}
Packit Service a9274b
Packit Service a9274b
	return 0;
Packit Service a9274b
}
Packit Service a9274b
Packit Service a9274b
int handleoutput(struct bat *bat, void *buffer, int bytes, int frames)
Packit Service a9274b
{
Packit Service a9274b
	int err = 0;
Packit Service a9274b
Packit Service a9274b
	/* If capture completed, terminate the playback */
Packit Service a9274b
	if (bat->periods_played * frames > 2 * bat->rate
Packit Service a9274b
			&& bat->latency.is_capturing == false)
Packit Service a9274b
		return bat->latency.state;
Packit Service a9274b
Packit Service a9274b
	if (bat->latency.state == LATENCY_STATE_PLAY_AND_LISTEN)
Packit Service a9274b
		err = generate_sine_wave(bat, frames, buffer);
Packit Service a9274b
	else
Packit Service a9274b
		/* Output silence */
Packit Service a9274b
		memset(buffer, 0, bytes);
Packit Service a9274b
Packit Service a9274b
	return err;
Packit Service a9274b
}