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