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 pa_assert(r);
69
70 soxr_clear(r->impl.data);
71 #else
72 /* With libsoxr prior to 0.1.2 soxr_clear() makes soxr_process() crash afterwards,
73 * so don't use this function and re-create the context instead. */
74 soxr_t old_state;
75
76 pa_assert(r);
77
78 old_state = r->impl.data;
79 r->impl.data = NULL;
80
81 if (pa_resampler_soxr_init(r) == 0) {
82 if (old_state)
83 soxr_delete(old_state);
84 } else {
85 r->impl.data = old_state;
86 pa_log_error("Failed to reset libsoxr context");
87 }
88 #endif
89 }
90
resampler_soxr_update_rates(pa_resampler * r)91 static void resampler_soxr_update_rates(pa_resampler *r) {
92 soxr_t old_state;
93
94 pa_assert(r);
95
96 /* There is no update method in libsoxr,
97 * so just re-create the resampler context */
98
99 old_state = r->impl.data;
100 r->impl.data = NULL;
101
102 if (pa_resampler_soxr_init(r) == 0) {
103 if (old_state)
104 soxr_delete(old_state);
105 } else {
106 r->impl.data = old_state;
107 pa_log_error("Failed to update libsoxr sample rates");
108 }
109 }
110
pa_resampler_soxr_init(pa_resampler * r)111 int pa_resampler_soxr_init(pa_resampler *r) {
112 soxr_t state;
113 soxr_datatype_t io_format;
114 soxr_io_spec_t io_spec;
115 soxr_runtime_spec_t runtime_spec;
116 unsigned long quality_recipe;
117 soxr_quality_spec_t quality;
118 soxr_error_t err = NULL;
119
120 pa_assert(r);
121
122 switch (r->work_format) {
123 case PA_SAMPLE_S16NE:
124 io_format = SOXR_INT16_I;
125 break;
126 case PA_SAMPLE_FLOAT32NE:
127 io_format = SOXR_FLOAT32_I;
128 break;
129 default:
130 pa_assert_not_reached();
131 }
132
133 io_spec = soxr_io_spec(io_format, io_format);
134
135 /* Resample in one thread. Multithreading makes
136 * performance worse with small chunks of audio. */
137 runtime_spec = soxr_runtime_spec(1);
138
139 switch (r->method) {
140 case PA_RESAMPLER_SOXR_MQ:
141 quality_recipe = SOXR_MQ | SOXR_LINEAR_PHASE;
142 break;
143 case PA_RESAMPLER_SOXR_HQ:
144 quality_recipe = SOXR_HQ | SOXR_LINEAR_PHASE;
145 break;
146 case PA_RESAMPLER_SOXR_VHQ:
147 quality_recipe = SOXR_VHQ | SOXR_LINEAR_PHASE;
148 break;
149 default:
150 pa_assert_not_reached();
151 }
152
153 quality = soxr_quality_spec(quality_recipe, 0);
154
155 state = soxr_create(r->i_ss.rate, r->o_ss.rate, r->work_channels, &err, &io_spec, &quality, &runtime_spec);
156 if (!state) {
157 pa_log_error("Failed to create libsoxr resampler context: %s.", (err ? err : "[unknown error]"));
158 return -1;
159 }
160
161 r->impl.free = resampler_soxr_free;
162 r->impl.reset = resampler_soxr_reset;
163 r->impl.update_rates = resampler_soxr_update_rates;
164 r->impl.resample = resampler_soxr_resample;
165 r->impl.data = state;
166
167 return 0;
168 }
169