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