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
30bool 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
57static 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
93static 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
119static 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
128static 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    speex_resampler_skip_zeros(state);
136}
137
138static void speex_free(pa_resampler *r) {
139    SpeexResamplerState *state;
140    pa_assert(r);
141
142    state = r->impl.data;
143    if (!state)
144        return;
145
146    speex_resampler_destroy(state);
147}
148
149int pa_resampler_speex_init(pa_resampler *r) {
150    int q, err;
151    SpeexResamplerState *state;
152
153    pa_assert(r);
154
155    r->impl.free = speex_free;
156    r->impl.update_rates = speex_update_rates;
157    r->impl.reset = speex_reset;
158
159    if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX) {
160
161        q = r->method - PA_RESAMPLER_SPEEX_FIXED_BASE;
162        r->impl.resample = speex_resample_int;
163
164    } else {
165        pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX);
166
167        q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE;
168        r->impl.resample = speex_resample_float;
169    }
170
171    pa_log_info("Choosing speex quality setting %i.", q);
172
173    if (!(state = speex_resampler_init(r->work_channels, r->i_ss.rate, r->o_ss.rate, q, &err)))
174        return -1;
175
176    speex_resampler_skip_zeros(state);
177
178    r->impl.data = state;
179
180    return 0;
181}
182