1/***
2  This file is part of PulseAudio.
3
4  Copyright 2014, 2015 Andrey Semashev
5
6  PulseAudio is free software; you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as published
8  by the Free Software Foundation; either version 2.1 of the License,
9  or (at your option) any later version.
10
11  PulseAudio is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  General Public License for more details.
15
16  You should have received a copy of the GNU Lesser General Public License
17  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <stddef.h>
25#include <soxr.h>
26
27#include <pulsecore/resampler.h>
28
29static unsigned resampler_soxr_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames,
30                                        pa_memchunk *output, unsigned *out_n_frames) {
31    soxr_t state;
32    void *in, *out;
33    size_t consumed = 0, produced = 0;
34
35    pa_assert(r);
36    pa_assert(input);
37    pa_assert(output);
38    pa_assert(out_n_frames);
39
40    state = r->impl.data;
41    pa_assert(state);
42
43    in = pa_memblock_acquire_chunk(input);
44    out = pa_memblock_acquire_chunk(output);
45
46    pa_assert_se(soxr_process(state, in, in_n_frames, &consumed, out, *out_n_frames, &produced) == 0);
47
48    pa_memblock_release(input->memblock);
49    pa_memblock_release(output->memblock);
50
51    *out_n_frames = produced;
52
53    return in_n_frames - consumed;
54}
55
56static void resampler_soxr_free(pa_resampler *r) {
57    pa_assert(r);
58
59    if (!r->impl.data)
60        return;
61
62    soxr_delete(r->impl.data);
63    r->impl.data = NULL;
64}
65
66static void resampler_soxr_reset(pa_resampler *r) {
67#if SOXR_THIS_VERSION >= SOXR_VERSION(0, 1, 2)
68    double ratio;
69
70    pa_assert(r);
71
72    soxr_clear(r->impl.data);
73
74    ratio = (double)r->i_ss.rate / (double)r->o_ss.rate;
75    soxr_set_io_ratio(r->impl.data, ratio, 0);
76#else
77    /* With libsoxr prior to 0.1.2 soxr_clear() makes soxr_process() crash afterwards,
78     * so don't use this function and re-create the context instead. */
79    soxr_t old_state;
80
81    pa_assert(r);
82
83    old_state = r->impl.data;
84    r->impl.data = NULL;
85
86    if (pa_resampler_soxr_init(r) == 0) {
87        if (old_state)
88            soxr_delete(old_state);
89    } else {
90        r->impl.data = old_state;
91        pa_log_error("Failed to reset libsoxr context");
92    }
93#endif
94}
95
96static void resampler_soxr_update_rates(pa_resampler *r) {
97    double ratio;
98
99    pa_assert(r);
100
101    ratio = (double)r->i_ss.rate / (double)r->o_ss.rate;
102    soxr_set_io_ratio(r->impl.data, ratio, 0);
103}
104
105int pa_resampler_soxr_init(pa_resampler *r) {
106    soxr_t state;
107    soxr_datatype_t io_format;
108    soxr_io_spec_t io_spec;
109    soxr_runtime_spec_t runtime_spec;
110    unsigned long quality_recipe;
111    soxr_quality_spec_t quality;
112    soxr_error_t err = NULL;
113    double ratio;
114
115    pa_assert(r);
116
117    switch (r->work_format) {
118        case PA_SAMPLE_S16NE:
119            io_format = SOXR_INT16_I;
120            break;
121        case PA_SAMPLE_FLOAT32NE:
122            io_format = SOXR_FLOAT32_I;
123            break;
124        default:
125            pa_assert_not_reached();
126    }
127
128    io_spec = soxr_io_spec(io_format, io_format);
129
130    /* Resample in one thread. Multithreading makes
131     * performance worse with small chunks of audio. */
132    runtime_spec = soxr_runtime_spec(1);
133
134    switch (r->method) {
135        case PA_RESAMPLER_SOXR_MQ:
136            quality_recipe = SOXR_MQ | SOXR_LINEAR_PHASE;
137            break;
138        case PA_RESAMPLER_SOXR_HQ:
139            quality_recipe = SOXR_HQ | SOXR_LINEAR_PHASE;
140            break;
141        case PA_RESAMPLER_SOXR_VHQ:
142            quality_recipe = SOXR_VHQ | SOXR_LINEAR_PHASE;
143            break;
144        default:
145            pa_assert_not_reached();
146    }
147
148    quality = soxr_quality_spec(quality_recipe, SOXR_VR);
149
150    /* Maximum resample ratio is 100:1 */
151    state = soxr_create(100, 1, r->work_channels, &err, &io_spec, &quality, &runtime_spec);
152    if (!state) {
153        pa_log_error("Failed to create libsoxr resampler context: %s.", (err ? err : "[unknown error]"));
154        return -1;
155    }
156
157    ratio = (double)r->i_ss.rate / (double)r->o_ss.rate;
158    soxr_set_io_ratio(state, ratio, 0);
159
160    r->impl.free = resampler_soxr_free;
161    r->impl.reset = resampler_soxr_reset;
162    r->impl.update_rates = resampler_soxr_update_rates;
163    r->impl.resample = resampler_soxr_resample;
164    r->impl.data = state;
165
166    return 0;
167}
168