153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2013 Collabora Ltd. 553a5a1b3Sopenharmony_ci Author: Arun Raghavan <arun.raghavan@collabora.co.uk> 653a5a1b3Sopenharmony_ci 753a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 853a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 953a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 1053a5a1b3Sopenharmony_ci or (at your option) any later version. 1153a5a1b3Sopenharmony_ci 1253a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1353a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1453a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1553a5a1b3Sopenharmony_ci General Public License for more details. 1653a5a1b3Sopenharmony_ci 1753a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1853a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1953a5a1b3Sopenharmony_ci***/ 2053a5a1b3Sopenharmony_ci 2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2253a5a1b3Sopenharmony_ci#include <config.h> 2353a5a1b3Sopenharmony_ci#endif 2453a5a1b3Sopenharmony_ci 2553a5a1b3Sopenharmony_ci#include <sys/types.h> 2653a5a1b3Sopenharmony_ci#include <sys/stat.h> 2753a5a1b3Sopenharmony_ci#include <fcntl.h> 2853a5a1b3Sopenharmony_ci 2953a5a1b3Sopenharmony_ci#include <errno.h> 3053a5a1b3Sopenharmony_ci#include <unistd.h> 3153a5a1b3Sopenharmony_ci#include <stdio.h> 3253a5a1b3Sopenharmony_ci#include <stdlib.h> 3353a5a1b3Sopenharmony_ci 3453a5a1b3Sopenharmony_ci#include <check.h> 3553a5a1b3Sopenharmony_ci 3653a5a1b3Sopenharmony_ci#include "lo-test-util.h" 3753a5a1b3Sopenharmony_ci 3853a5a1b3Sopenharmony_ci#define SAMPLE_HZ 44100 3953a5a1b3Sopenharmony_ci#define CHANNELS 2 4053a5a1b3Sopenharmony_ci#define N_OUT (SAMPLE_HZ * 1) 4153a5a1b3Sopenharmony_ci 4253a5a1b3Sopenharmony_cistatic float out[N_OUT][CHANNELS]; 4353a5a1b3Sopenharmony_ci 4453a5a1b3Sopenharmony_cipa_lo_test_context test_ctx; 4553a5a1b3Sopenharmony_cistatic const char *context_name = NULL; 4653a5a1b3Sopenharmony_ci 4753a5a1b3Sopenharmony_cistatic struct timeval tv_out, tv_in; 4853a5a1b3Sopenharmony_ci 4953a5a1b3Sopenharmony_cistatic void nop_free_cb(void *p) { 5053a5a1b3Sopenharmony_ci} 5153a5a1b3Sopenharmony_ci 5253a5a1b3Sopenharmony_cistatic void write_cb(pa_stream *s, size_t nbytes, void *userdata) { 5353a5a1b3Sopenharmony_ci pa_lo_test_context *ctx = (pa_lo_test_context *) userdata; 5453a5a1b3Sopenharmony_ci static int ppos = 0; 5553a5a1b3Sopenharmony_ci int r, nsamp; 5653a5a1b3Sopenharmony_ci 5753a5a1b3Sopenharmony_ci /* Get the real requested bytes since the last write might have been 5853a5a1b3Sopenharmony_ci * incomplete if it caused a wrap around */ 5953a5a1b3Sopenharmony_ci nbytes = pa_stream_writable_size(s); 6053a5a1b3Sopenharmony_ci nsamp = nbytes / ctx->fs; 6153a5a1b3Sopenharmony_ci 6253a5a1b3Sopenharmony_ci if (ppos + nsamp > N_OUT) { 6353a5a1b3Sopenharmony_ci /* Wrap-around, write to end and exit. Next iteration will fill up the 6453a5a1b3Sopenharmony_ci * rest */ 6553a5a1b3Sopenharmony_ci nbytes = (N_OUT - ppos) * ctx->fs; 6653a5a1b3Sopenharmony_ci } 6753a5a1b3Sopenharmony_ci 6853a5a1b3Sopenharmony_ci if (ppos == 0) 6953a5a1b3Sopenharmony_ci pa_gettimeofday(&tv_out); 7053a5a1b3Sopenharmony_ci 7153a5a1b3Sopenharmony_ci r = pa_stream_write(s, &out[ppos][0], nbytes, nop_free_cb, 0, PA_SEEK_RELATIVE); 7253a5a1b3Sopenharmony_ci fail_unless(r == 0); 7353a5a1b3Sopenharmony_ci 7453a5a1b3Sopenharmony_ci ppos = (ppos + nbytes / ctx->fs) % N_OUT; 7553a5a1b3Sopenharmony_ci} 7653a5a1b3Sopenharmony_ci 7753a5a1b3Sopenharmony_ci#define WINDOW (2 * CHANNELS) 7853a5a1b3Sopenharmony_ci 7953a5a1b3Sopenharmony_cistatic void read_cb(pa_stream *s, size_t nbytes, void *userdata) { 8053a5a1b3Sopenharmony_ci pa_lo_test_context *ctx = (pa_lo_test_context *) userdata; 8153a5a1b3Sopenharmony_ci static float last = 0.0f; 8253a5a1b3Sopenharmony_ci const float *in; 8353a5a1b3Sopenharmony_ci float cur; 8453a5a1b3Sopenharmony_ci int r; 8553a5a1b3Sopenharmony_ci unsigned int i = 0; 8653a5a1b3Sopenharmony_ci size_t l; 8753a5a1b3Sopenharmony_ci 8853a5a1b3Sopenharmony_ci r = pa_stream_peek(s, (const void **)&in, &l); 8953a5a1b3Sopenharmony_ci fail_unless(r == 0); 9053a5a1b3Sopenharmony_ci 9153a5a1b3Sopenharmony_ci if (l == 0) 9253a5a1b3Sopenharmony_ci return; 9353a5a1b3Sopenharmony_ci 9453a5a1b3Sopenharmony_ci#if 0 9553a5a1b3Sopenharmony_ci { 9653a5a1b3Sopenharmony_ci static int fd = -1; 9753a5a1b3Sopenharmony_ci 9853a5a1b3Sopenharmony_ci if (fd == -1) { 9953a5a1b3Sopenharmony_ci fd = open("loopback.raw", O_CREAT | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR); 10053a5a1b3Sopenharmony_ci fail_if(fd < 0); 10153a5a1b3Sopenharmony_ci } 10253a5a1b3Sopenharmony_ci 10353a5a1b3Sopenharmony_ci r = write(fd, in, l); 10453a5a1b3Sopenharmony_ci } 10553a5a1b3Sopenharmony_ci#endif 10653a5a1b3Sopenharmony_ci 10753a5a1b3Sopenharmony_ci do { 10853a5a1b3Sopenharmony_ci#if 0 10953a5a1b3Sopenharmony_ci { 11053a5a1b3Sopenharmony_ci int j; 11153a5a1b3Sopenharmony_ci fprintf(stderr, "%g (", pa_rms(in, WINDOW)); 11253a5a1b3Sopenharmony_ci for (j = 0; j < WINDOW; j++) 11353a5a1b3Sopenharmony_ci fprintf(stderr, "%g ", in[j]); 11453a5a1b3Sopenharmony_ci fprintf(stderr, ")\n"); 11553a5a1b3Sopenharmony_ci } 11653a5a1b3Sopenharmony_ci#endif 11753a5a1b3Sopenharmony_ci if (i + (ctx->ss * WINDOW) < l) 11853a5a1b3Sopenharmony_ci cur = pa_rms(in, WINDOW); 11953a5a1b3Sopenharmony_ci else 12053a5a1b3Sopenharmony_ci cur = pa_rms(in, (l - i) / ctx->ss); 12153a5a1b3Sopenharmony_ci 12253a5a1b3Sopenharmony_ci /* We leave the definition of 0 generous since the window might 12353a5a1b3Sopenharmony_ci * straddle the 0->1 transition, raising the average power. We keep the 12453a5a1b3Sopenharmony_ci * definition of 1 tight in this case and detect the transition in the 12553a5a1b3Sopenharmony_ci * next round. */ 12653a5a1b3Sopenharmony_ci if (cur - last > 0.4f) { 12753a5a1b3Sopenharmony_ci pa_gettimeofday(&tv_in); 12853a5a1b3Sopenharmony_ci fprintf(stderr, "Latency %llu\n", (unsigned long long) pa_timeval_diff(&tv_in, &tv_out)); 12953a5a1b3Sopenharmony_ci } 13053a5a1b3Sopenharmony_ci 13153a5a1b3Sopenharmony_ci last = cur; 13253a5a1b3Sopenharmony_ci in += WINDOW; 13353a5a1b3Sopenharmony_ci i += ctx->ss * WINDOW; 13453a5a1b3Sopenharmony_ci } while (i + (ctx->ss * WINDOW) <= l); 13553a5a1b3Sopenharmony_ci 13653a5a1b3Sopenharmony_ci pa_stream_drop(s); 13753a5a1b3Sopenharmony_ci} 13853a5a1b3Sopenharmony_ci 13953a5a1b3Sopenharmony_ciSTART_TEST (loopback_test) { 14053a5a1b3Sopenharmony_ci int i, pulse_hz = SAMPLE_HZ / 1000; 14153a5a1b3Sopenharmony_ci 14253a5a1b3Sopenharmony_ci test_ctx.context_name = context_name; 14353a5a1b3Sopenharmony_ci 14453a5a1b3Sopenharmony_ci test_ctx.sample_spec.format = PA_SAMPLE_FLOAT32, 14553a5a1b3Sopenharmony_ci test_ctx.sample_spec.rate = SAMPLE_HZ, 14653a5a1b3Sopenharmony_ci test_ctx.sample_spec.channels = CHANNELS, 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci test_ctx.play_latency = 25; 14953a5a1b3Sopenharmony_ci test_ctx.rec_latency = 5; 15053a5a1b3Sopenharmony_ci 15153a5a1b3Sopenharmony_ci test_ctx.read_cb = read_cb; 15253a5a1b3Sopenharmony_ci test_ctx.write_cb = write_cb; 15353a5a1b3Sopenharmony_ci 15453a5a1b3Sopenharmony_ci /* Generate a square pulse */ 15553a5a1b3Sopenharmony_ci for (i = 0; i < N_OUT; i++) 15653a5a1b3Sopenharmony_ci if (i < pulse_hz) 15753a5a1b3Sopenharmony_ci out[i][0] = out[i][1] = 1.0f; 15853a5a1b3Sopenharmony_ci else 15953a5a1b3Sopenharmony_ci out[i][0] = out[i][1] = 0.0f; 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_ci fail_unless(pa_lo_test_init(&test_ctx) == 0); 16253a5a1b3Sopenharmony_ci fail_unless(pa_lo_test_run(&test_ctx) == 0); 16353a5a1b3Sopenharmony_ci pa_lo_test_deinit(&test_ctx); 16453a5a1b3Sopenharmony_ci} 16553a5a1b3Sopenharmony_ciEND_TEST 16653a5a1b3Sopenharmony_ci 16753a5a1b3Sopenharmony_ciint main(int argc, char *argv[]) { 16853a5a1b3Sopenharmony_ci int failed = 0; 16953a5a1b3Sopenharmony_ci Suite *s; 17053a5a1b3Sopenharmony_ci TCase *tc; 17153a5a1b3Sopenharmony_ci SRunner *sr; 17253a5a1b3Sopenharmony_ci 17353a5a1b3Sopenharmony_ci context_name = argv[0]; 17453a5a1b3Sopenharmony_ci 17553a5a1b3Sopenharmony_ci s = suite_create("Loopback latency"); 17653a5a1b3Sopenharmony_ci tc = tcase_create("loopback latency"); 17753a5a1b3Sopenharmony_ci tcase_add_test(tc, loopback_test); 17853a5a1b3Sopenharmony_ci tcase_set_timeout(tc, 5 * 60); 17953a5a1b3Sopenharmony_ci suite_add_tcase(s, tc); 18053a5a1b3Sopenharmony_ci 18153a5a1b3Sopenharmony_ci sr = srunner_create(s); 18253a5a1b3Sopenharmony_ci srunner_set_fork_status(sr, CK_NOFORK); 18353a5a1b3Sopenharmony_ci srunner_run_all(sr, CK_NORMAL); 18453a5a1b3Sopenharmony_ci failed = srunner_ntests_failed(sr); 18553a5a1b3Sopenharmony_ci srunner_free(sr); 18653a5a1b3Sopenharmony_ci 18753a5a1b3Sopenharmony_ci return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 18853a5a1b3Sopenharmony_ci} 189