1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
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 <speex/speex_resampler.h>
25 #include <math.h>
26
27 #include <pulsecore/once.h>
28 #include <pulsecore/resampler.h>
29
pa_speex_is_fixed_point(void)30 bool pa_speex_is_fixed_point(void) {
31 static bool result = false;
32 PA_ONCE_BEGIN {
33 float f_out = -1.0f, f_in = 1.0f;
34 spx_uint32_t in_len = 1, out_len = 1;
35 SpeexResamplerState *s;
36
37 pa_assert_se(s = speex_resampler_init(1, 1, 1,
38 SPEEX_RESAMPLER_QUALITY_MIN, NULL));
39
40 /* feed one sample that is too soft for fixed-point speex */
41 pa_assert_se(speex_resampler_process_float(s, 0, &f_in, &in_len,
42 &f_out, &out_len) == RESAMPLER_ERR_SUCCESS);
43
44 /* expecting sample has been processed, one sample output */
45 pa_assert_se(in_len == 1 && out_len == 1);
46
47 /* speex compiled with --enable-fixed-point will output 0.0 due to insufficient precision */
48 if (fabsf(f_out) < 0.00001f)
49 result = true;
50
51 speex_resampler_destroy(s);
52 } PA_ONCE_END;
53 return result;
54 }
55
56
speex_resample_float(pa_resampler * r,const pa_memchunk * input,unsigned in_n_frames,pa_memchunk * output,unsigned * out_n_frames)57 static unsigned speex_resample_float(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
58 float *in, *out;
59 uint32_t inf = in_n_frames, outf = *out_n_frames;
60 SpeexResamplerState *state;
61
62 pa_assert(r);
63 pa_assert(input);
64 pa_assert(output);
65 pa_assert(out_n_frames);
66
67 state = r->impl.data;
68
69 in = pa_memblock_acquire_chunk(input);
70 out = pa_memblock_acquire_chunk(output);
71
72 /* Strictly speaking, speex resampler expects its input
73 * to be normalized to the [-32768.0 .. 32767.0] range.
74 * This matters if speex has been compiled with --enable-fixed-point,
75 * because such speex will round the samples to the nearest
76 * integer. speex with --enable-fixed-point is therefore incompatible
77 * with PulseAudio's floating-point sample range [-1 .. 1]. speex
78 * without --enable-fixed-point works fine with this range.
79 * Care has been taken to call speex_resample_float() only
80 * for speex compiled without --enable-fixed-point.
81 */
82 pa_assert_se(speex_resampler_process_interleaved_float(state, in, &inf, out, &outf) == 0);
83
84 pa_memblock_release(input->memblock);
85 pa_memblock_release(output->memblock);
86
87 pa_assert(inf == in_n_frames);
88 *out_n_frames = outf;
89
90 return 0;
91 }
92
speex_resample_int(pa_resampler * r,const pa_memchunk * input,unsigned in_n_frames,pa_memchunk * output,unsigned * out_n_frames)93 static unsigned speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
94 int16_t *in, *out;
95 uint32_t inf = in_n_frames, outf = *out_n_frames;
96 SpeexResamplerState *state;
97
98 pa_assert(r);
99 pa_assert(input);
100 pa_assert(output);
101 pa_assert(out_n_frames);
102
103 state = r->impl.data;
104
105 in = pa_memblock_acquire_chunk(input);
106 out = pa_memblock_acquire_chunk(output);
107
108 pa_assert_se(speex_resampler_process_interleaved_int(state, in, &inf, out, &outf) == 0);
109
110 pa_memblock_release(input->memblock);
111 pa_memblock_release(output->memblock);
112
113 pa_assert(inf == in_n_frames);
114 *out_n_frames = outf;
115
116 return 0;
117 }
118
speex_update_rates(pa_resampler * r)119 static void speex_update_rates(pa_resampler *r) {
120 SpeexResamplerState *state;
121 pa_assert(r);
122
123 state = r->impl.data;
124
125 pa_assert_se(speex_resampler_set_rate(state, r->i_ss.rate, r->o_ss.rate) == 0);
126 }
127
speex_reset(pa_resampler * r)128 static void speex_reset(pa_resampler *r) {
129 SpeexResamplerState *state;
130 pa_assert(r);
131
132 state = r->impl.data;
133
134 pa_assert_se(speex_resampler_reset_mem(state) == 0);
135 }
136
speex_free(pa_resampler * r)137 static void speex_free(pa_resampler *r) {
138 SpeexResamplerState *state;
139 pa_assert(r);
140
141 state = r->impl.data;
142 if (!state)
143 return;
144
145 speex_resampler_destroy(state);
146 }
147
pa_resampler_speex_init(pa_resampler * r)148 int pa_resampler_speex_init(pa_resampler *r) {
149 int q, err;
150 SpeexResamplerState *state;
151
152 pa_assert(r);
153
154 r->impl.free = speex_free;
155 r->impl.update_rates = speex_update_rates;
156 r->impl.reset = speex_reset;
157
158 if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX) {
159
160 q = r->method - PA_RESAMPLER_SPEEX_FIXED_BASE;
161 r->impl.resample = speex_resample_int;
162
163 } else {
164 pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
165
166 q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
167 r->impl.resample = speex_resample_float;
168 }
169
170 pa_log_info("Choosing speex quality setting %i.", q);
171
172 if (!(state = speex_resampler_init(r->work_channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
173 return -1;
174
175 r->impl.data = state;
176
177 return 0;
178 }
179