153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2014, 2015 Andrey Semashev 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as published 853a5a1b3Sopenharmony_ci by the Free Software Foundation; either version 2.1 of the License, 953a5a1b3Sopenharmony_ci or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public License 1753a5a1b3Sopenharmony_ci along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <stddef.h> 2553a5a1b3Sopenharmony_ci#include <soxr.h> 2653a5a1b3Sopenharmony_ci 2753a5a1b3Sopenharmony_ci#include <pulsecore/resampler.h> 2853a5a1b3Sopenharmony_ci 2953a5a1b3Sopenharmony_cistatic unsigned resampler_soxr_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, 3053a5a1b3Sopenharmony_ci pa_memchunk *output, unsigned *out_n_frames) { 3153a5a1b3Sopenharmony_ci soxr_t state; 3253a5a1b3Sopenharmony_ci void *in, *out; 3353a5a1b3Sopenharmony_ci size_t consumed = 0, produced = 0; 3453a5a1b3Sopenharmony_ci 3553a5a1b3Sopenharmony_ci pa_assert(r); 3653a5a1b3Sopenharmony_ci pa_assert(input); 3753a5a1b3Sopenharmony_ci pa_assert(output); 3853a5a1b3Sopenharmony_ci pa_assert(out_n_frames); 3953a5a1b3Sopenharmony_ci 4053a5a1b3Sopenharmony_ci state = r->impl.data; 4153a5a1b3Sopenharmony_ci pa_assert(state); 4253a5a1b3Sopenharmony_ci 4353a5a1b3Sopenharmony_ci in = pa_memblock_acquire_chunk(input); 4453a5a1b3Sopenharmony_ci out = pa_memblock_acquire_chunk(output); 4553a5a1b3Sopenharmony_ci 4653a5a1b3Sopenharmony_ci pa_assert_se(soxr_process(state, in, in_n_frames, &consumed, out, *out_n_frames, &produced) == 0); 4753a5a1b3Sopenharmony_ci 4853a5a1b3Sopenharmony_ci pa_memblock_release(input->memblock); 4953a5a1b3Sopenharmony_ci pa_memblock_release(output->memblock); 5053a5a1b3Sopenharmony_ci 5153a5a1b3Sopenharmony_ci *out_n_frames = produced; 5253a5a1b3Sopenharmony_ci 5353a5a1b3Sopenharmony_ci return in_n_frames - consumed; 5453a5a1b3Sopenharmony_ci} 5553a5a1b3Sopenharmony_ci 5653a5a1b3Sopenharmony_cistatic void resampler_soxr_free(pa_resampler *r) { 5753a5a1b3Sopenharmony_ci pa_assert(r); 5853a5a1b3Sopenharmony_ci 5953a5a1b3Sopenharmony_ci if (!r->impl.data) 6053a5a1b3Sopenharmony_ci return; 6153a5a1b3Sopenharmony_ci 6253a5a1b3Sopenharmony_ci soxr_delete(r->impl.data); 6353a5a1b3Sopenharmony_ci r->impl.data = NULL; 6453a5a1b3Sopenharmony_ci} 6553a5a1b3Sopenharmony_ci 6653a5a1b3Sopenharmony_cistatic void resampler_soxr_reset(pa_resampler *r) { 6753a5a1b3Sopenharmony_ci#if SOXR_THIS_VERSION >= SOXR_VERSION(0, 1, 2) 6853a5a1b3Sopenharmony_ci double ratio; 6953a5a1b3Sopenharmony_ci 7053a5a1b3Sopenharmony_ci pa_assert(r); 7153a5a1b3Sopenharmony_ci 7253a5a1b3Sopenharmony_ci soxr_clear(r->impl.data); 7353a5a1b3Sopenharmony_ci 7453a5a1b3Sopenharmony_ci ratio = (double)r->i_ss.rate / (double)r->o_ss.rate; 7553a5a1b3Sopenharmony_ci soxr_set_io_ratio(r->impl.data, ratio, 0); 7653a5a1b3Sopenharmony_ci#else 7753a5a1b3Sopenharmony_ci /* With libsoxr prior to 0.1.2 soxr_clear() makes soxr_process() crash afterwards, 7853a5a1b3Sopenharmony_ci * so don't use this function and re-create the context instead. */ 7953a5a1b3Sopenharmony_ci soxr_t old_state; 8053a5a1b3Sopenharmony_ci 8153a5a1b3Sopenharmony_ci pa_assert(r); 8253a5a1b3Sopenharmony_ci 8353a5a1b3Sopenharmony_ci old_state = r->impl.data; 8453a5a1b3Sopenharmony_ci r->impl.data = NULL; 8553a5a1b3Sopenharmony_ci 8653a5a1b3Sopenharmony_ci if (pa_resampler_soxr_init(r) == 0) { 8753a5a1b3Sopenharmony_ci if (old_state) 8853a5a1b3Sopenharmony_ci soxr_delete(old_state); 8953a5a1b3Sopenharmony_ci } else { 9053a5a1b3Sopenharmony_ci r->impl.data = old_state; 9153a5a1b3Sopenharmony_ci pa_log_error("Failed to reset libsoxr context"); 9253a5a1b3Sopenharmony_ci } 9353a5a1b3Sopenharmony_ci#endif 9453a5a1b3Sopenharmony_ci} 9553a5a1b3Sopenharmony_ci 9653a5a1b3Sopenharmony_cistatic void resampler_soxr_update_rates(pa_resampler *r) { 9753a5a1b3Sopenharmony_ci double ratio; 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_ci pa_assert(r); 10053a5a1b3Sopenharmony_ci 10153a5a1b3Sopenharmony_ci ratio = (double)r->i_ss.rate / (double)r->o_ss.rate; 10253a5a1b3Sopenharmony_ci soxr_set_io_ratio(r->impl.data, ratio, 0); 10353a5a1b3Sopenharmony_ci} 10453a5a1b3Sopenharmony_ci 10553a5a1b3Sopenharmony_ciint pa_resampler_soxr_init(pa_resampler *r) { 10653a5a1b3Sopenharmony_ci soxr_t state; 10753a5a1b3Sopenharmony_ci soxr_datatype_t io_format; 10853a5a1b3Sopenharmony_ci soxr_io_spec_t io_spec; 10953a5a1b3Sopenharmony_ci soxr_runtime_spec_t runtime_spec; 11053a5a1b3Sopenharmony_ci unsigned long quality_recipe; 11153a5a1b3Sopenharmony_ci soxr_quality_spec_t quality; 11253a5a1b3Sopenharmony_ci soxr_error_t err = NULL; 11353a5a1b3Sopenharmony_ci double ratio; 11453a5a1b3Sopenharmony_ci 11553a5a1b3Sopenharmony_ci pa_assert(r); 11653a5a1b3Sopenharmony_ci 11753a5a1b3Sopenharmony_ci switch (r->work_format) { 11853a5a1b3Sopenharmony_ci case PA_SAMPLE_S16NE: 11953a5a1b3Sopenharmony_ci io_format = SOXR_INT16_I; 12053a5a1b3Sopenharmony_ci break; 12153a5a1b3Sopenharmony_ci case PA_SAMPLE_FLOAT32NE: 12253a5a1b3Sopenharmony_ci io_format = SOXR_FLOAT32_I; 12353a5a1b3Sopenharmony_ci break; 12453a5a1b3Sopenharmony_ci default: 12553a5a1b3Sopenharmony_ci pa_assert_not_reached(); 12653a5a1b3Sopenharmony_ci } 12753a5a1b3Sopenharmony_ci 12853a5a1b3Sopenharmony_ci io_spec = soxr_io_spec(io_format, io_format); 12953a5a1b3Sopenharmony_ci 13053a5a1b3Sopenharmony_ci /* Resample in one thread. Multithreading makes 13153a5a1b3Sopenharmony_ci * performance worse with small chunks of audio. */ 13253a5a1b3Sopenharmony_ci runtime_spec = soxr_runtime_spec(1); 13353a5a1b3Sopenharmony_ci 13453a5a1b3Sopenharmony_ci switch (r->method) { 13553a5a1b3Sopenharmony_ci case PA_RESAMPLER_SOXR_MQ: 13653a5a1b3Sopenharmony_ci quality_recipe = SOXR_MQ | SOXR_LINEAR_PHASE; 13753a5a1b3Sopenharmony_ci break; 13853a5a1b3Sopenharmony_ci case PA_RESAMPLER_SOXR_HQ: 13953a5a1b3Sopenharmony_ci quality_recipe = SOXR_HQ | SOXR_LINEAR_PHASE; 14053a5a1b3Sopenharmony_ci break; 14153a5a1b3Sopenharmony_ci case PA_RESAMPLER_SOXR_VHQ: 14253a5a1b3Sopenharmony_ci quality_recipe = SOXR_VHQ | SOXR_LINEAR_PHASE; 14353a5a1b3Sopenharmony_ci break; 14453a5a1b3Sopenharmony_ci default: 14553a5a1b3Sopenharmony_ci pa_assert_not_reached(); 14653a5a1b3Sopenharmony_ci } 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci quality = soxr_quality_spec(quality_recipe, SOXR_VR); 14953a5a1b3Sopenharmony_ci 15053a5a1b3Sopenharmony_ci /* Maximum resample ratio is 100:1 */ 15153a5a1b3Sopenharmony_ci state = soxr_create(100, 1, r->work_channels, &err, &io_spec, &quality, &runtime_spec); 15253a5a1b3Sopenharmony_ci if (!state) { 15353a5a1b3Sopenharmony_ci pa_log_error("Failed to create libsoxr resampler context: %s.", (err ? err : "[unknown error]")); 15453a5a1b3Sopenharmony_ci return -1; 15553a5a1b3Sopenharmony_ci } 15653a5a1b3Sopenharmony_ci 15753a5a1b3Sopenharmony_ci ratio = (double)r->i_ss.rate / (double)r->o_ss.rate; 15853a5a1b3Sopenharmony_ci soxr_set_io_ratio(state, ratio, 0); 15953a5a1b3Sopenharmony_ci 16053a5a1b3Sopenharmony_ci r->impl.free = resampler_soxr_free; 16153a5a1b3Sopenharmony_ci r->impl.reset = resampler_soxr_reset; 16253a5a1b3Sopenharmony_ci r->impl.update_rates = resampler_soxr_update_rates; 16353a5a1b3Sopenharmony_ci r->impl.resample = resampler_soxr_resample; 16453a5a1b3Sopenharmony_ci r->impl.data = state; 16553a5a1b3Sopenharmony_ci 16653a5a1b3Sopenharmony_ci return 0; 16753a5a1b3Sopenharmony_ci} 168