153a5a1b3Sopenharmony_ci/***
253a5a1b3Sopenharmony_ci  This file is part of PulseAudio.
353a5a1b3Sopenharmony_ci
453a5a1b3Sopenharmony_ci  Copyright 2014, 2015 Andrey Semashev
553a5a1b3Sopenharmony_ci
653a5a1b3Sopenharmony_ci  PulseAudio is free software; you can redistribute it and/or modify
753a5a1b3Sopenharmony_ci  it under the terms of the GNU Lesser General Public License as published
853a5a1b3Sopenharmony_ci  by the Free Software Foundation; either version 2.1 of the License,
953a5a1b3Sopenharmony_ci  or (at your option) any later version.
1053a5a1b3Sopenharmony_ci
1153a5a1b3Sopenharmony_ci  PulseAudio is distributed in the hope that it will be useful, but
1253a5a1b3Sopenharmony_ci  WITHOUT ANY WARRANTY; without even the implied warranty of
1353a5a1b3Sopenharmony_ci  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1453a5a1b3Sopenharmony_ci  General Public License for more details.
1553a5a1b3Sopenharmony_ci
1653a5a1b3Sopenharmony_ci  You should have received a copy of the GNU Lesser General Public License
1753a5a1b3Sopenharmony_ci  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
1853a5a1b3Sopenharmony_ci***/
1953a5a1b3Sopenharmony_ci
2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H
2153a5a1b3Sopenharmony_ci#include <config.h>
2253a5a1b3Sopenharmony_ci#endif
2353a5a1b3Sopenharmony_ci
2453a5a1b3Sopenharmony_ci#include <stddef.h>
2553a5a1b3Sopenharmony_ci#include <soxr.h>
2653a5a1b3Sopenharmony_ci
2753a5a1b3Sopenharmony_ci#include <pulsecore/resampler.h>
2853a5a1b3Sopenharmony_ci
2953a5a1b3Sopenharmony_cistatic unsigned resampler_soxr_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames,
3053a5a1b3Sopenharmony_ci                                        pa_memchunk *output, unsigned *out_n_frames) {
3153a5a1b3Sopenharmony_ci    soxr_t state;
3253a5a1b3Sopenharmony_ci    void *in, *out;
3353a5a1b3Sopenharmony_ci    size_t consumed = 0, produced = 0;
3453a5a1b3Sopenharmony_ci
3553a5a1b3Sopenharmony_ci    pa_assert(r);
3653a5a1b3Sopenharmony_ci    pa_assert(input);
3753a5a1b3Sopenharmony_ci    pa_assert(output);
3853a5a1b3Sopenharmony_ci    pa_assert(out_n_frames);
3953a5a1b3Sopenharmony_ci
4053a5a1b3Sopenharmony_ci    state = r->impl.data;
4153a5a1b3Sopenharmony_ci    pa_assert(state);
4253a5a1b3Sopenharmony_ci
4353a5a1b3Sopenharmony_ci    in = pa_memblock_acquire_chunk(input);
4453a5a1b3Sopenharmony_ci    out = pa_memblock_acquire_chunk(output);
4553a5a1b3Sopenharmony_ci
4653a5a1b3Sopenharmony_ci    pa_assert_se(soxr_process(state, in, in_n_frames, &consumed, out, *out_n_frames, &produced) == 0);
4753a5a1b3Sopenharmony_ci
4853a5a1b3Sopenharmony_ci    pa_memblock_release(input->memblock);
4953a5a1b3Sopenharmony_ci    pa_memblock_release(output->memblock);
5053a5a1b3Sopenharmony_ci
5153a5a1b3Sopenharmony_ci    *out_n_frames = produced;
5253a5a1b3Sopenharmony_ci
5353a5a1b3Sopenharmony_ci    return in_n_frames - consumed;
5453a5a1b3Sopenharmony_ci}
5553a5a1b3Sopenharmony_ci
5653a5a1b3Sopenharmony_cistatic void resampler_soxr_free(pa_resampler *r) {
5753a5a1b3Sopenharmony_ci    pa_assert(r);
5853a5a1b3Sopenharmony_ci
5953a5a1b3Sopenharmony_ci    if (!r->impl.data)
6053a5a1b3Sopenharmony_ci        return;
6153a5a1b3Sopenharmony_ci
6253a5a1b3Sopenharmony_ci    soxr_delete(r->impl.data);
6353a5a1b3Sopenharmony_ci    r->impl.data = NULL;
6453a5a1b3Sopenharmony_ci}
6553a5a1b3Sopenharmony_ci
6653a5a1b3Sopenharmony_cistatic void resampler_soxr_reset(pa_resampler *r) {
6753a5a1b3Sopenharmony_ci#if SOXR_THIS_VERSION >= SOXR_VERSION(0, 1, 2)
6853a5a1b3Sopenharmony_ci    double ratio;
6953a5a1b3Sopenharmony_ci
7053a5a1b3Sopenharmony_ci    pa_assert(r);
7153a5a1b3Sopenharmony_ci
7253a5a1b3Sopenharmony_ci    soxr_clear(r->impl.data);
7353a5a1b3Sopenharmony_ci
7453a5a1b3Sopenharmony_ci    ratio = (double)r->i_ss.rate / (double)r->o_ss.rate;
7553a5a1b3Sopenharmony_ci    soxr_set_io_ratio(r->impl.data, ratio, 0);
7653a5a1b3Sopenharmony_ci#else
7753a5a1b3Sopenharmony_ci    /* With libsoxr prior to 0.1.2 soxr_clear() makes soxr_process() crash afterwards,
7853a5a1b3Sopenharmony_ci     * so don't use this function and re-create the context instead. */
7953a5a1b3Sopenharmony_ci    soxr_t old_state;
8053a5a1b3Sopenharmony_ci
8153a5a1b3Sopenharmony_ci    pa_assert(r);
8253a5a1b3Sopenharmony_ci
8353a5a1b3Sopenharmony_ci    old_state = r->impl.data;
8453a5a1b3Sopenharmony_ci    r->impl.data = NULL;
8553a5a1b3Sopenharmony_ci
8653a5a1b3Sopenharmony_ci    if (pa_resampler_soxr_init(r) == 0) {
8753a5a1b3Sopenharmony_ci        if (old_state)
8853a5a1b3Sopenharmony_ci            soxr_delete(old_state);
8953a5a1b3Sopenharmony_ci    } else {
9053a5a1b3Sopenharmony_ci        r->impl.data = old_state;
9153a5a1b3Sopenharmony_ci        pa_log_error("Failed to reset libsoxr context");
9253a5a1b3Sopenharmony_ci    }
9353a5a1b3Sopenharmony_ci#endif
9453a5a1b3Sopenharmony_ci}
9553a5a1b3Sopenharmony_ci
9653a5a1b3Sopenharmony_cistatic void resampler_soxr_update_rates(pa_resampler *r) {
9753a5a1b3Sopenharmony_ci    double ratio;
9853a5a1b3Sopenharmony_ci
9953a5a1b3Sopenharmony_ci    pa_assert(r);
10053a5a1b3Sopenharmony_ci
10153a5a1b3Sopenharmony_ci    ratio = (double)r->i_ss.rate / (double)r->o_ss.rate;
10253a5a1b3Sopenharmony_ci    soxr_set_io_ratio(r->impl.data, ratio, 0);
10353a5a1b3Sopenharmony_ci}
10453a5a1b3Sopenharmony_ci
10553a5a1b3Sopenharmony_ciint pa_resampler_soxr_init(pa_resampler *r) {
10653a5a1b3Sopenharmony_ci    soxr_t state;
10753a5a1b3Sopenharmony_ci    soxr_datatype_t io_format;
10853a5a1b3Sopenharmony_ci    soxr_io_spec_t io_spec;
10953a5a1b3Sopenharmony_ci    soxr_runtime_spec_t runtime_spec;
11053a5a1b3Sopenharmony_ci    unsigned long quality_recipe;
11153a5a1b3Sopenharmony_ci    soxr_quality_spec_t quality;
11253a5a1b3Sopenharmony_ci    soxr_error_t err = NULL;
11353a5a1b3Sopenharmony_ci    double ratio;
11453a5a1b3Sopenharmony_ci
11553a5a1b3Sopenharmony_ci    pa_assert(r);
11653a5a1b3Sopenharmony_ci
11753a5a1b3Sopenharmony_ci    switch (r->work_format) {
11853a5a1b3Sopenharmony_ci        case PA_SAMPLE_S16NE:
11953a5a1b3Sopenharmony_ci            io_format = SOXR_INT16_I;
12053a5a1b3Sopenharmony_ci            break;
12153a5a1b3Sopenharmony_ci        case PA_SAMPLE_FLOAT32NE:
12253a5a1b3Sopenharmony_ci            io_format = SOXR_FLOAT32_I;
12353a5a1b3Sopenharmony_ci            break;
12453a5a1b3Sopenharmony_ci        default:
12553a5a1b3Sopenharmony_ci            pa_assert_not_reached();
12653a5a1b3Sopenharmony_ci    }
12753a5a1b3Sopenharmony_ci
12853a5a1b3Sopenharmony_ci    io_spec = soxr_io_spec(io_format, io_format);
12953a5a1b3Sopenharmony_ci
13053a5a1b3Sopenharmony_ci    /* Resample in one thread. Multithreading makes
13153a5a1b3Sopenharmony_ci     * performance worse with small chunks of audio. */
13253a5a1b3Sopenharmony_ci    runtime_spec = soxr_runtime_spec(1);
13353a5a1b3Sopenharmony_ci
13453a5a1b3Sopenharmony_ci    switch (r->method) {
13553a5a1b3Sopenharmony_ci        case PA_RESAMPLER_SOXR_MQ:
13653a5a1b3Sopenharmony_ci            quality_recipe = SOXR_MQ | SOXR_LINEAR_PHASE;
13753a5a1b3Sopenharmony_ci            break;
13853a5a1b3Sopenharmony_ci        case PA_RESAMPLER_SOXR_HQ:
13953a5a1b3Sopenharmony_ci            quality_recipe = SOXR_HQ | SOXR_LINEAR_PHASE;
14053a5a1b3Sopenharmony_ci            break;
14153a5a1b3Sopenharmony_ci        case PA_RESAMPLER_SOXR_VHQ:
14253a5a1b3Sopenharmony_ci            quality_recipe = SOXR_VHQ | SOXR_LINEAR_PHASE;
14353a5a1b3Sopenharmony_ci            break;
14453a5a1b3Sopenharmony_ci        default:
14553a5a1b3Sopenharmony_ci            pa_assert_not_reached();
14653a5a1b3Sopenharmony_ci    }
14753a5a1b3Sopenharmony_ci
14853a5a1b3Sopenharmony_ci    quality = soxr_quality_spec(quality_recipe, SOXR_VR);
14953a5a1b3Sopenharmony_ci
15053a5a1b3Sopenharmony_ci    /* Maximum resample ratio is 100:1 */
15153a5a1b3Sopenharmony_ci    state = soxr_create(100, 1, r->work_channels, &err, &io_spec, &quality, &runtime_spec);
15253a5a1b3Sopenharmony_ci    if (!state) {
15353a5a1b3Sopenharmony_ci        pa_log_error("Failed to create libsoxr resampler context: %s.", (err ? err : "[unknown error]"));
15453a5a1b3Sopenharmony_ci        return -1;
15553a5a1b3Sopenharmony_ci    }
15653a5a1b3Sopenharmony_ci
15753a5a1b3Sopenharmony_ci    ratio = (double)r->i_ss.rate / (double)r->o_ss.rate;
15853a5a1b3Sopenharmony_ci    soxr_set_io_ratio(state, ratio, 0);
15953a5a1b3Sopenharmony_ci
16053a5a1b3Sopenharmony_ci    r->impl.free = resampler_soxr_free;
16153a5a1b3Sopenharmony_ci    r->impl.reset = resampler_soxr_reset;
16253a5a1b3Sopenharmony_ci    r->impl.update_rates = resampler_soxr_update_rates;
16353a5a1b3Sopenharmony_ci    r->impl.resample = resampler_soxr_resample;
16453a5a1b3Sopenharmony_ci    r->impl.data = state;
16553a5a1b3Sopenharmony_ci
16653a5a1b3Sopenharmony_ci    return 0;
16753a5a1b3Sopenharmony_ci}
168