153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
553a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
653a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
753a5a1b3Sopenharmony_ci  or (at your option) any later version.
853a5a1b3Sopenharmony_ci
953a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1053a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1153a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1253a5a1b3Sopenharmony_ci  General Public License for more details.
1353a5a1b3Sopenharmony_ci
1453a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1553a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1653a5a1b3Sopenharmony_ci***/
1753a5a1b3Sopenharmony_ci
1853a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
1953a5a1b3Sopenharmony_ci#include <config.h>
2053a5a1b3Sopenharmony_ci#endif
2153a5a1b3Sopenharmony_ci
2253a5a1b3Sopenharmony_ci#include <stdio.h>
2353a5a1b3Sopenharmony_ci#include <getopt.h>
2453a5a1b3Sopenharmony_ci#include <locale.h>
2553a5a1b3Sopenharmony_ci#include <math.h>
2653a5a1b3Sopenharmony_ci
2753a5a1b3Sopenharmony_ci#include <pulse/pulseaudio.h>
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_ci#include <pulse/rtclock.h>
3053a5a1b3Sopenharmony_ci#include <pulse/sample.h>
3153a5a1b3Sopenharmony_ci#include <pulse/volume.h>
3253a5a1b3Sopenharmony_ci
3353a5a1b3Sopenharmony_ci#include <pulsecore/i18n.h>
3453a5a1b3Sopenharmony_ci#include <pulsecore/log.h>
3553a5a1b3Sopenharmony_ci#include <pulsecore/resampler.h>
3653a5a1b3Sopenharmony_ci#include <pulsecore/macro.h>
3753a5a1b3Sopenharmony_ci#include <pulsecore/endianmacros.h>
3853a5a1b3Sopenharmony_ci#include <pulsecore/memblock.h>
3953a5a1b3Sopenharmony_ci#include <pulsecore/memblockq.h>
4053a5a1b3Sopenharmony_ci#include <pulsecore/sample-util.h>
4153a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h>
4253a5a1b3Sopenharmony_ci
4353a5a1b3Sopenharmony_ci#define MEMBLOCKQ_MAXLENGTH (16*1024*1024)
4453a5a1b3Sopenharmony_ci#define PA_SILENCE_MAX (pa_page_size()*16)
4553a5a1b3Sopenharmony_ci#define MAX_MATCHING_PERIOD 500
4653a5a1b3Sopenharmony_ci
4753a5a1b3Sopenharmony_cistatic pa_memblock *silence_memblock_new(pa_mempool *pool, uint8_t c) {
4853a5a1b3Sopenharmony_ci    pa_memblock *b;
4953a5a1b3Sopenharmony_ci    size_t length;
5053a5a1b3Sopenharmony_ci    void *data;
5153a5a1b3Sopenharmony_ci
5253a5a1b3Sopenharmony_ci    pa_assert(pool);
5353a5a1b3Sopenharmony_ci
5453a5a1b3Sopenharmony_ci    length = PA_MIN(pa_mempool_block_size_max(pool), PA_SILENCE_MAX);
5553a5a1b3Sopenharmony_ci
5653a5a1b3Sopenharmony_ci    b = pa_memblock_new(pool, length);
5753a5a1b3Sopenharmony_ci
5853a5a1b3Sopenharmony_ci    data = pa_memblock_acquire(b);
5953a5a1b3Sopenharmony_ci    memset(data, c, length);
6053a5a1b3Sopenharmony_ci    pa_memblock_release(b);
6153a5a1b3Sopenharmony_ci
6253a5a1b3Sopenharmony_ci    pa_memblock_set_is_silence(b, true);
6353a5a1b3Sopenharmony_ci
6453a5a1b3Sopenharmony_ci    return b;
6553a5a1b3Sopenharmony_ci}
6653a5a1b3Sopenharmony_ci
6753a5a1b3Sopenharmony_ci/* Calculate number of history bytes needed for the rewind */
6853a5a1b3Sopenharmony_cistatic size_t calculate_resampler_history_bytes(pa_resampler *r, size_t in_rewind_frames) {
6953a5a1b3Sopenharmony_ci    size_t history_frames, history_max, matching_period, total_frames, remainder;
7053a5a1b3Sopenharmony_ci    double delay;
7153a5a1b3Sopenharmony_ci
7253a5a1b3Sopenharmony_ci    if (!r)
7353a5a1b3Sopenharmony_ci        return 0;
7453a5a1b3Sopenharmony_ci
7553a5a1b3Sopenharmony_ci    /* Initialize some variables, cut off full seconds from the rewind */
7653a5a1b3Sopenharmony_ci    total_frames = 0;
7753a5a1b3Sopenharmony_ci    in_rewind_frames = in_rewind_frames % r->i_ss.rate;
7853a5a1b3Sopenharmony_ci    history_max = (uint64_t) PA_RESAMPLER_MAX_DELAY_USEC * r->i_ss.rate * 3 / PA_USEC_PER_SEC / 2;
7953a5a1b3Sopenharmony_ci
8053a5a1b3Sopenharmony_ci    /* Get the current internal delay of the resampler */
8153a5a1b3Sopenharmony_ci    delay = pa_resampler_get_delay(r, false);
8253a5a1b3Sopenharmony_ci
8353a5a1b3Sopenharmony_ci    /* Calculate the matchiung period */
8453a5a1b3Sopenharmony_ci    matching_period = r->i_ss.rate / pa_resampler_get_gcd(r);
8553a5a1b3Sopenharmony_ci    pa_log_debug("Integral period length is %lu input frames", matching_period);
8653a5a1b3Sopenharmony_ci
8753a5a1b3Sopenharmony_ci    /* If the delay is larger than the length of the history queue, we can only
8853a5a1b3Sopenharmony_ci     * replay as much as we have. */
8953a5a1b3Sopenharmony_ci    if ((size_t)delay >= history_max) {
9053a5a1b3Sopenharmony_ci        history_frames = history_max;
9153a5a1b3Sopenharmony_ci        return history_frames * r->i_fz;
9253a5a1b3Sopenharmony_ci    }
9353a5a1b3Sopenharmony_ci
9453a5a1b3Sopenharmony_ci    /* Initially set the history to 3 times the resampler delay. Use at least 2 ms. */
9553a5a1b3Sopenharmony_ci    history_frames = (size_t)(delay * 3.0);
9653a5a1b3Sopenharmony_ci    history_frames = PA_MAX(history_frames, r->i_ss.rate / 500);
9753a5a1b3Sopenharmony_ci
9853a5a1b3Sopenharmony_ci    /* Check how the rewind fits into multiples of the matching period. */
9953a5a1b3Sopenharmony_ci    remainder = (in_rewind_frames + history_frames) % matching_period;
10053a5a1b3Sopenharmony_ci
10153a5a1b3Sopenharmony_ci    /* If possible, use between 2 and 3 times the resampler delay */
10253a5a1b3Sopenharmony_ci    if (remainder < (size_t)delay && history_frames - remainder <= history_max)
10353a5a1b3Sopenharmony_ci        total_frames = in_rewind_frames + history_frames - remainder;
10453a5a1b3Sopenharmony_ci
10553a5a1b3Sopenharmony_ci    /* Else, try above 3 times the delay */
10653a5a1b3Sopenharmony_ci    else if (history_frames + matching_period - remainder <= history_max)
10753a5a1b3Sopenharmony_ci        total_frames = in_rewind_frames + history_frames + matching_period - remainder;
10853a5a1b3Sopenharmony_ci
10953a5a1b3Sopenharmony_ci    if (total_frames != 0)
11053a5a1b3Sopenharmony_ci        /* We found a perfect match. */
11153a5a1b3Sopenharmony_ci        history_frames = total_frames - in_rewind_frames;
11253a5a1b3Sopenharmony_ci    else {
11353a5a1b3Sopenharmony_ci        /* Try to use 2.5 times the delay. */
11453a5a1b3Sopenharmony_ci        history_frames = PA_MIN((size_t)(delay * 2.5), history_max);
11553a5a1b3Sopenharmony_ci        pa_log_debug("No usable integral matching period");
11653a5a1b3Sopenharmony_ci    }
11753a5a1b3Sopenharmony_ci
11853a5a1b3Sopenharmony_ci    return history_frames * r->i_fz;
11953a5a1b3Sopenharmony_ci}
12053a5a1b3Sopenharmony_ci
12153a5a1b3Sopenharmony_cistatic float compare_blocks(const pa_sample_spec *ss, const pa_memchunk *chunk_a, const pa_memchunk *chunk_b) {
12253a5a1b3Sopenharmony_ci    float *a, *b, max_diff = 0;
12353a5a1b3Sopenharmony_ci    unsigned i;
12453a5a1b3Sopenharmony_ci
12553a5a1b3Sopenharmony_ci    a = pa_memblock_acquire(chunk_a->memblock);
12653a5a1b3Sopenharmony_ci    b = pa_memblock_acquire(chunk_b->memblock);
12753a5a1b3Sopenharmony_ci    a += chunk_a->index / pa_frame_size(ss);
12853a5a1b3Sopenharmony_ci    b += chunk_b->index / pa_frame_size(ss);
12953a5a1b3Sopenharmony_ci
13053a5a1b3Sopenharmony_ci    for (i = 0; i < chunk_a->length / pa_frame_size(ss); i++) {
13153a5a1b3Sopenharmony_ci        if (fabs(a[i] - b[i]) > max_diff)
13253a5a1b3Sopenharmony_ci            max_diff = fabs(a[i] - b[i]);
13353a5a1b3Sopenharmony_ci    }
13453a5a1b3Sopenharmony_ci
13553a5a1b3Sopenharmony_ci    pa_memblock_release(chunk_a->memblock);
13653a5a1b3Sopenharmony_ci    pa_memblock_release(chunk_b->memblock);
13753a5a1b3Sopenharmony_ci
13853a5a1b3Sopenharmony_ci    return max_diff;
13953a5a1b3Sopenharmony_ci}
14053a5a1b3Sopenharmony_ci
14153a5a1b3Sopenharmony_cistatic pa_memblock* generate_block(pa_mempool *pool, const pa_sample_spec *ss, unsigned frequency, double amplitude, size_t nr_of_samples) {
14253a5a1b3Sopenharmony_ci    pa_memblock *r;
14353a5a1b3Sopenharmony_ci    float *d;
14453a5a1b3Sopenharmony_ci    float val;
14553a5a1b3Sopenharmony_ci    unsigned i;
14653a5a1b3Sopenharmony_ci    int n;
14753a5a1b3Sopenharmony_ci    float t, dt, dt_period;
14853a5a1b3Sopenharmony_ci
14953a5a1b3Sopenharmony_ci    pa_assert(frequency);
15053a5a1b3Sopenharmony_ci    pa_assert(nr_of_samples);
15153a5a1b3Sopenharmony_ci    pa_assert(ss->channels == 1);
15253a5a1b3Sopenharmony_ci    pa_assert(ss->format == PA_SAMPLE_FLOAT32NE);
15353a5a1b3Sopenharmony_ci
15453a5a1b3Sopenharmony_ci    pa_assert_se(r = pa_memblock_new(pool, pa_frame_size(ss) * nr_of_samples));
15553a5a1b3Sopenharmony_ci    d = pa_memblock_acquire(r);
15653a5a1b3Sopenharmony_ci
15753a5a1b3Sopenharmony_ci    /* Generate square wave with given length, frequency and sample rate. */
15853a5a1b3Sopenharmony_ci    val = amplitude;
15953a5a1b3Sopenharmony_ci    t = 0;
16053a5a1b3Sopenharmony_ci    n = 1;
16153a5a1b3Sopenharmony_ci    dt = 1.0 / ss->rate;
16253a5a1b3Sopenharmony_ci    dt_period = 1.0 / frequency;
16353a5a1b3Sopenharmony_ci    for (i=0; i < nr_of_samples; i++) {
16453a5a1b3Sopenharmony_ci        d[i] = val;
16553a5a1b3Sopenharmony_ci
16653a5a1b3Sopenharmony_ci        if ((int)(2 * t / dt_period) >= n) {
16753a5a1b3Sopenharmony_ci            n++;
16853a5a1b3Sopenharmony_ci            if (val >= amplitude)
16953a5a1b3Sopenharmony_ci                val = - amplitude;
17053a5a1b3Sopenharmony_ci            else
17153a5a1b3Sopenharmony_ci                val = amplitude;
17253a5a1b3Sopenharmony_ci        }
17353a5a1b3Sopenharmony_ci
17453a5a1b3Sopenharmony_ci        t += dt;
17553a5a1b3Sopenharmony_ci    }
17653a5a1b3Sopenharmony_ci
17753a5a1b3Sopenharmony_ci    pa_memblock_release(r);
17853a5a1b3Sopenharmony_ci
17953a5a1b3Sopenharmony_ci    return r;
18053a5a1b3Sopenharmony_ci}
18153a5a1b3Sopenharmony_ci
18253a5a1b3Sopenharmony_cistatic void help(const char *argv0) {
18353a5a1b3Sopenharmony_ci    printf("%s [options]\n\n"
18453a5a1b3Sopenharmony_ci           "-h, --help                            Show this help\n"
18553a5a1b3Sopenharmony_ci           "-v, --verbose                         Print debug messages\n"
18653a5a1b3Sopenharmony_ci           "      --from-rate=SAMPLERATE          From sample rate in Hz (defaults to 44100)\n"
18753a5a1b3Sopenharmony_ci           "      --to-rate=SAMPLERATE            To sample rate in Hz (defaults to 44100)\n"
18853a5a1b3Sopenharmony_ci           "      --resample-method=METHOD        Resample method (defaults to auto)\n"
18953a5a1b3Sopenharmony_ci           "      --frequency=unsigned            Frequency of square wave\n"
19053a5a1b3Sopenharmony_ci           "      --samples=unsigned              Number of samples for square wave\n"
19153a5a1b3Sopenharmony_ci           "      --rewind=unsigned               Number of output samples to rewind\n"
19253a5a1b3Sopenharmony_ci           "\n"
19353a5a1b3Sopenharmony_ci           "This test generates samples for a square wave of given frequency, number of samples\n"
19453a5a1b3Sopenharmony_ci           "and input sample rate. Then this input data is resampled to the output rate, rewound\n"
19553a5a1b3Sopenharmony_ci           "by rewind samples and the rewound part is processed again. Then output is compared to\n"
19653a5a1b3Sopenharmony_ci           "the result of the first pass.\n"
19753a5a1b3Sopenharmony_ci           "\n"
19853a5a1b3Sopenharmony_ci           "See --dump-resample-methods for possible values of resample methods.\n",
19953a5a1b3Sopenharmony_ci           argv0);
20053a5a1b3Sopenharmony_ci}
20153a5a1b3Sopenharmony_ci
20253a5a1b3Sopenharmony_cienum {
20353a5a1b3Sopenharmony_ci    ARG_VERSION = 256,
20453a5a1b3Sopenharmony_ci    ARG_FROM_SAMPLERATE,
20553a5a1b3Sopenharmony_ci    ARG_TO_SAMPLERATE,
20653a5a1b3Sopenharmony_ci    ARG_FREQUENCY,
20753a5a1b3Sopenharmony_ci    ARG_SAMPLES,
20853a5a1b3Sopenharmony_ci    ARG_REWIND,
20953a5a1b3Sopenharmony_ci    ARG_RESAMPLE_METHOD,
21053a5a1b3Sopenharmony_ci    ARG_DUMP_RESAMPLE_METHODS
21153a5a1b3Sopenharmony_ci};
21253a5a1b3Sopenharmony_ci
21353a5a1b3Sopenharmony_cistatic void dump_resample_methods(void) {
21453a5a1b3Sopenharmony_ci    int i;
21553a5a1b3Sopenharmony_ci
21653a5a1b3Sopenharmony_ci    for (i = 0; i < PA_RESAMPLER_MAX; i++)
21753a5a1b3Sopenharmony_ci        if (pa_resample_method_supported(i))
21853a5a1b3Sopenharmony_ci            printf("%s\n", pa_resample_method_to_string(i));
21953a5a1b3Sopenharmony_ci
22053a5a1b3Sopenharmony_ci}
22153a5a1b3Sopenharmony_ci
22253a5a1b3Sopenharmony_ciint main(int argc, char *argv[]) {
22353a5a1b3Sopenharmony_ci    pa_mempool *pool = NULL;
22453a5a1b3Sopenharmony_ci    pa_sample_spec a, b;
22553a5a1b3Sopenharmony_ci    pa_resample_method_t method;
22653a5a1b3Sopenharmony_ci    int ret = 1, c;
22753a5a1b3Sopenharmony_ci    unsigned samples, frequency, rewind;
22853a5a1b3Sopenharmony_ci    unsigned crossover_freq = 120;
22953a5a1b3Sopenharmony_ci    pa_resampler *resampler;
23053a5a1b3Sopenharmony_ci    pa_memchunk in_chunk, out_chunk, rewound_chunk, silence_chunk;
23153a5a1b3Sopenharmony_ci    pa_usec_t ts;
23253a5a1b3Sopenharmony_ci    pa_memblockq *history_queue = NULL;
23353a5a1b3Sopenharmony_ci    size_t in_rewind_size, in_frame_size, history_size, out_rewind_size, old_length, in_resampler_buffer, n_out_expected;
23453a5a1b3Sopenharmony_ci    float max_diff;
23553a5a1b3Sopenharmony_ci    double delay_before, delay_after, delay_expected;
23653a5a1b3Sopenharmony_ci
23753a5a1b3Sopenharmony_ci    static const struct option long_options[] = {
23853a5a1b3Sopenharmony_ci        {"help",                  0, NULL, 'h'},
23953a5a1b3Sopenharmony_ci        {"verbose",               0, NULL, 'v'},
24053a5a1b3Sopenharmony_ci        {"version",               0, NULL, ARG_VERSION},
24153a5a1b3Sopenharmony_ci        {"from-rate",             1, NULL, ARG_FROM_SAMPLERATE},
24253a5a1b3Sopenharmony_ci        {"to-rate",               1, NULL, ARG_TO_SAMPLERATE},
24353a5a1b3Sopenharmony_ci        {"frequency",             1, NULL, ARG_FREQUENCY},
24453a5a1b3Sopenharmony_ci        {"samples",               1, NULL, ARG_SAMPLES},
24553a5a1b3Sopenharmony_ci        {"rewind",                1, NULL, ARG_REWIND},
24653a5a1b3Sopenharmony_ci        {"resample-method",       1, NULL, ARG_RESAMPLE_METHOD},
24753a5a1b3Sopenharmony_ci        {"dump-resample-methods", 0, NULL, ARG_DUMP_RESAMPLE_METHODS},
24853a5a1b3Sopenharmony_ci        {NULL,                    0, NULL, 0}
24953a5a1b3Sopenharmony_ci    };
25053a5a1b3Sopenharmony_ci
25153a5a1b3Sopenharmony_ci    setlocale(LC_ALL, "");
25253a5a1b3Sopenharmony_ci#ifdef ENABLE_NLS
25353a5a1b3Sopenharmony_ci    bindtextdomain(GETTEXT_PACKAGE, PULSE_LOCALEDIR);
25453a5a1b3Sopenharmony_ci#endif
25553a5a1b3Sopenharmony_ci
25653a5a1b3Sopenharmony_ci    pa_log_set_level(PA_LOG_WARN);
25753a5a1b3Sopenharmony_ci    if (!getenv("MAKE_CHECK"))
25853a5a1b3Sopenharmony_ci        pa_log_set_level(PA_LOG_INFO);
25953a5a1b3Sopenharmony_ci
26053a5a1b3Sopenharmony_ci    a.channels = b.channels = 1;
26153a5a1b3Sopenharmony_ci    a.rate = 48000;
26253a5a1b3Sopenharmony_ci    b.rate = 44100;
26353a5a1b3Sopenharmony_ci    a.format = b.format = PA_SAMPLE_FLOAT32NE;
26453a5a1b3Sopenharmony_ci
26553a5a1b3Sopenharmony_ci    method = PA_RESAMPLER_AUTO;
26653a5a1b3Sopenharmony_ci    frequency = 1000;
26753a5a1b3Sopenharmony_ci    samples = 5000;
26853a5a1b3Sopenharmony_ci    rewind = 2500;
26953a5a1b3Sopenharmony_ci
27053a5a1b3Sopenharmony_ci    while ((c = getopt_long(argc, argv, "hv", long_options, NULL)) != -1) {
27153a5a1b3Sopenharmony_ci
27253a5a1b3Sopenharmony_ci        switch (c) {
27353a5a1b3Sopenharmony_ci            case 'h' :
27453a5a1b3Sopenharmony_ci                help(argv[0]);
27553a5a1b3Sopenharmony_ci                ret = 0;
27653a5a1b3Sopenharmony_ci                goto quit;
27753a5a1b3Sopenharmony_ci
27853a5a1b3Sopenharmony_ci            case 'v':
27953a5a1b3Sopenharmony_ci                pa_log_set_level(PA_LOG_DEBUG);
28053a5a1b3Sopenharmony_ci                break;
28153a5a1b3Sopenharmony_ci
28253a5a1b3Sopenharmony_ci            case ARG_VERSION:
28353a5a1b3Sopenharmony_ci                printf("%s %s\n", argv[0], PACKAGE_VERSION);
28453a5a1b3Sopenharmony_ci                ret = 0;
28553a5a1b3Sopenharmony_ci                goto quit;
28653a5a1b3Sopenharmony_ci
28753a5a1b3Sopenharmony_ci            case ARG_DUMP_RESAMPLE_METHODS:
28853a5a1b3Sopenharmony_ci                dump_resample_methods();
28953a5a1b3Sopenharmony_ci                ret = 0;
29053a5a1b3Sopenharmony_ci                goto quit;
29153a5a1b3Sopenharmony_ci
29253a5a1b3Sopenharmony_ci            case ARG_FROM_SAMPLERATE:
29353a5a1b3Sopenharmony_ci                a.rate = (uint32_t) atoi(optarg);
29453a5a1b3Sopenharmony_ci                break;
29553a5a1b3Sopenharmony_ci
29653a5a1b3Sopenharmony_ci            case ARG_TO_SAMPLERATE:
29753a5a1b3Sopenharmony_ci                b.rate = (uint32_t) atoi(optarg);
29853a5a1b3Sopenharmony_ci                break;
29953a5a1b3Sopenharmony_ci
30053a5a1b3Sopenharmony_ci            case ARG_FREQUENCY:
30153a5a1b3Sopenharmony_ci                frequency = (unsigned) atoi(optarg);
30253a5a1b3Sopenharmony_ci                break;
30353a5a1b3Sopenharmony_ci
30453a5a1b3Sopenharmony_ci            case ARG_SAMPLES:
30553a5a1b3Sopenharmony_ci                samples = (unsigned) atoi(optarg);
30653a5a1b3Sopenharmony_ci                break;
30753a5a1b3Sopenharmony_ci
30853a5a1b3Sopenharmony_ci            case ARG_REWIND:
30953a5a1b3Sopenharmony_ci                rewind = (unsigned) atoi(optarg);
31053a5a1b3Sopenharmony_ci                break;
31153a5a1b3Sopenharmony_ci
31253a5a1b3Sopenharmony_ci            case ARG_RESAMPLE_METHOD:
31353a5a1b3Sopenharmony_ci                if (*optarg == '\0' || pa_streq(optarg, "help")) {
31453a5a1b3Sopenharmony_ci                    dump_resample_methods();
31553a5a1b3Sopenharmony_ci                    ret = 0;
31653a5a1b3Sopenharmony_ci                    goto quit;
31753a5a1b3Sopenharmony_ci                }
31853a5a1b3Sopenharmony_ci                method = pa_parse_resample_method(optarg);
31953a5a1b3Sopenharmony_ci                break;
32053a5a1b3Sopenharmony_ci
32153a5a1b3Sopenharmony_ci            default:
32253a5a1b3Sopenharmony_ci                goto quit;
32353a5a1b3Sopenharmony_ci        }
32453a5a1b3Sopenharmony_ci    }
32553a5a1b3Sopenharmony_ci
32653a5a1b3Sopenharmony_ci    pa_log_info("=== Square wave %u Hz, %u samples. Resampling using %s from %u Hz to %u Hz, rewinding %u output samples.", frequency,
32753a5a1b3Sopenharmony_ci                   samples, pa_resample_method_to_string(method), a.rate, b.rate, rewind);
32853a5a1b3Sopenharmony_ci
32953a5a1b3Sopenharmony_ci    ret = 0;
33053a5a1b3Sopenharmony_ci    pa_assert_se(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true));
33153a5a1b3Sopenharmony_ci
33253a5a1b3Sopenharmony_ci    pa_log_debug("Compilation CFLAGS: %s", PA_CFLAGS);
33353a5a1b3Sopenharmony_ci
33453a5a1b3Sopenharmony_ci    /* Setup resampler */
33553a5a1b3Sopenharmony_ci    ts = pa_rtclock_now();
33653a5a1b3Sopenharmony_ci    pa_assert_se(resampler = pa_resampler_new(pool, &a, NULL, &b, NULL, crossover_freq, method, 0));
33753a5a1b3Sopenharmony_ci    pa_log_info("Init took %llu usec", (long long unsigned)(pa_rtclock_now() - ts));
33853a5a1b3Sopenharmony_ci
33953a5a1b3Sopenharmony_ci    /* Generate input data */
34053a5a1b3Sopenharmony_ci    in_chunk.memblock = generate_block(pool, &a, frequency, 0.5, samples);
34153a5a1b3Sopenharmony_ci    in_chunk.length = pa_memblock_get_length(in_chunk.memblock);
34253a5a1b3Sopenharmony_ci    in_chunk.index = 0;
34353a5a1b3Sopenharmony_ci    in_frame_size = pa_frame_size(&a);
34453a5a1b3Sopenharmony_ci
34553a5a1b3Sopenharmony_ci    /* First, resample the full block */
34653a5a1b3Sopenharmony_ci    ts = pa_rtclock_now();
34753a5a1b3Sopenharmony_ci    pa_resampler_run(resampler, &in_chunk, &out_chunk);
34853a5a1b3Sopenharmony_ci    if (!out_chunk.memblock) {
34953a5a1b3Sopenharmony_ci        pa_memblock_unref(in_chunk.memblock);
35053a5a1b3Sopenharmony_ci        pa_log_warn("Resampling did not return any output data");
35153a5a1b3Sopenharmony_ci        ret = 1;
35253a5a1b3Sopenharmony_ci        goto quit;
35353a5a1b3Sopenharmony_ci    }
35453a5a1b3Sopenharmony_ci
35553a5a1b3Sopenharmony_ci    pa_log_info("resampling took %llu usec.", (long long unsigned)(pa_rtclock_now() - ts));
35653a5a1b3Sopenharmony_ci    if (rewind > out_chunk.length / pa_frame_size(&b)) {
35753a5a1b3Sopenharmony_ci        pa_log_warn("Specified number of frames to rewind (%u) larger than number of output frames (%lu), aborting.", rewind, out_chunk.length / pa_frame_size(&b));
35853a5a1b3Sopenharmony_ci        ret = 1;
35953a5a1b3Sopenharmony_ci        goto quit1;
36053a5a1b3Sopenharmony_ci    }
36153a5a1b3Sopenharmony_ci
36253a5a1b3Sopenharmony_ci    /* Get delay after first resampling pass */
36353a5a1b3Sopenharmony_ci    delay_before = pa_resampler_get_delay(resampler, true);
36453a5a1b3Sopenharmony_ci
36553a5a1b3Sopenharmony_ci    /* Create and prepare history queue */
36653a5a1b3Sopenharmony_ci    silence_chunk.memblock = silence_memblock_new(pool, 0);
36753a5a1b3Sopenharmony_ci    silence_chunk.length = pa_frame_align(pa_memblock_get_length(silence_chunk.memblock), &a);
36853a5a1b3Sopenharmony_ci    silence_chunk.index = 0;
36953a5a1b3Sopenharmony_ci    history_queue = pa_memblockq_new("Test-Queue", 0, MEMBLOCKQ_MAXLENGTH, 0, &a, 0, 1, samples * in_frame_size, &silence_chunk);
37053a5a1b3Sopenharmony_ci    pa_memblock_unref(silence_chunk.memblock);
37153a5a1b3Sopenharmony_ci
37253a5a1b3Sopenharmony_ci    pa_memblockq_push(history_queue, &in_chunk);
37353a5a1b3Sopenharmony_ci    pa_memblockq_drop(history_queue, samples * in_frame_size);
37453a5a1b3Sopenharmony_ci
37553a5a1b3Sopenharmony_ci    in_rewind_size = pa_resampler_request(resampler, rewind * pa_frame_size(&b));
37653a5a1b3Sopenharmony_ci    out_rewind_size = rewind * pa_frame_size(&b);
37753a5a1b3Sopenharmony_ci    pa_log_debug("Have to rewind %lu input frames", in_rewind_size / in_frame_size);
37853a5a1b3Sopenharmony_ci    ts = pa_rtclock_now();
37953a5a1b3Sopenharmony_ci
38053a5a1b3Sopenharmony_ci    /* Now rewind the resampler */
38153a5a1b3Sopenharmony_ci    pa_memblockq_rewind(history_queue, in_rewind_size);
38253a5a1b3Sopenharmony_ci    history_size = calculate_resampler_history_bytes(resampler, in_rewind_size / in_frame_size);
38353a5a1b3Sopenharmony_ci    pa_log_debug("History is %lu frames.", history_size / in_frame_size);
38453a5a1b3Sopenharmony_ci    pa_resampler_rewind(resampler, out_rewind_size, history_queue, history_size);
38553a5a1b3Sopenharmony_ci
38653a5a1b3Sopenharmony_ci    pa_log_info("Rewind took %llu usec.", (long long unsigned)(pa_rtclock_now() - ts));
38753a5a1b3Sopenharmony_ci    ts = pa_rtclock_now();
38853a5a1b3Sopenharmony_ci
38953a5a1b3Sopenharmony_ci    /* Re-run the resampler */
39053a5a1b3Sopenharmony_ci    old_length = in_chunk.length;
39153a5a1b3Sopenharmony_ci    in_chunk.length = in_rewind_size;
39253a5a1b3Sopenharmony_ci    in_chunk.index = old_length - in_chunk.length;
39353a5a1b3Sopenharmony_ci    pa_resampler_run(resampler, &in_chunk, &rewound_chunk);
39453a5a1b3Sopenharmony_ci    if (!rewound_chunk.memblock) {
39553a5a1b3Sopenharmony_ci        pa_log_warn("Resampler did not return output data for rewind");
39653a5a1b3Sopenharmony_ci        ret = 1;
39753a5a1b3Sopenharmony_ci        goto quit1;
39853a5a1b3Sopenharmony_ci    }
39953a5a1b3Sopenharmony_ci
40053a5a1b3Sopenharmony_ci    /* Get delay after rewind */
40153a5a1b3Sopenharmony_ci    delay_after = pa_resampler_get_delay(resampler, true);
40253a5a1b3Sopenharmony_ci
40353a5a1b3Sopenharmony_ci    /* Calculate expected delay */
40453a5a1b3Sopenharmony_ci    n_out_expected = pa_resampler_result(resampler, in_rewind_size + history_size) / pa_frame_size(&b);
40553a5a1b3Sopenharmony_ci    delay_expected = delay_before + (double)(in_rewind_size + history_size) / (double)in_frame_size - n_out_expected * (double)a.rate / (double)b.rate;
40653a5a1b3Sopenharmony_ci
40753a5a1b3Sopenharmony_ci    /* Check for leftover samples in the resampler buffer */
40853a5a1b3Sopenharmony_ci    in_resampler_buffer = lround((delay_after - delay_expected) * (double)b.rate / (double)a.rate);
40953a5a1b3Sopenharmony_ci    if (in_resampler_buffer != 0) {
41053a5a1b3Sopenharmony_ci        pa_log_debug("%li output frames still in resampler buffer", in_resampler_buffer);
41153a5a1b3Sopenharmony_ci    }
41253a5a1b3Sopenharmony_ci
41353a5a1b3Sopenharmony_ci    pa_log_info("Second resampler run took %llu usec.", (long long unsigned)(pa_rtclock_now() - ts));
41453a5a1b3Sopenharmony_ci    pa_log_debug("Got %lu output frames", rewound_chunk.length / pa_frame_size(&b));
41553a5a1b3Sopenharmony_ci    old_length = out_chunk.length;
41653a5a1b3Sopenharmony_ci    out_chunk.length = rewound_chunk.length;
41753a5a1b3Sopenharmony_ci    out_chunk.index = old_length - out_chunk.length;
41853a5a1b3Sopenharmony_ci
41953a5a1b3Sopenharmony_ci    max_diff = compare_blocks(&b, &out_chunk, &rewound_chunk);
42053a5a1b3Sopenharmony_ci    pa_log_info("Maximum difference is %.*g", 6, max_diff);
42153a5a1b3Sopenharmony_ci
42253a5a1b3Sopenharmony_ci    pa_memblock_unref(rewound_chunk.memblock);
42353a5a1b3Sopenharmony_ci
42453a5a1b3Sopenharmony_ciquit1:
42553a5a1b3Sopenharmony_ci    pa_memblock_unref(in_chunk.memblock);
42653a5a1b3Sopenharmony_ci    pa_memblock_unref(out_chunk.memblock);
42753a5a1b3Sopenharmony_ci
42853a5a1b3Sopenharmony_ci    pa_resampler_free(resampler);
42953a5a1b3Sopenharmony_ci    if (history_queue)
43053a5a1b3Sopenharmony_ci        pa_memblockq_free(history_queue);
43153a5a1b3Sopenharmony_ci
43253a5a1b3Sopenharmony_ciquit:
43353a5a1b3Sopenharmony_ci    if (pool)
43453a5a1b3Sopenharmony_ci        pa_mempool_unref(pool);
43553a5a1b3Sopenharmony_ci
43653a5a1b3Sopenharmony_ci    return ret;
43753a5a1b3Sopenharmony_ci}
438