• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
resampler_soxr_resample(pa_resampler * r,const pa_memchunk * input,unsigned in_n_frames,pa_memchunk * output,unsigned * out_n_frames)29 static 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 
resampler_soxr_free(pa_resampler * r)56 static 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 
resampler_soxr_reset(pa_resampler * r)66 static 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 
resampler_soxr_update_rates(pa_resampler * r)96 static 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 
pa_resampler_soxr_init(pa_resampler * r)105 int 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