X-Git-Url: https://code.delx.au/pulseaudio/blobdiff_plain/4bd9737725b85d90a7cf12b82528c2de70a7fbfe..be101476113b18fbdb32d9905ec8fe88b9d34df1:/src/pulsecore/resampler.c diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index 78ad5530..2842e653 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -5,7 +5,7 @@ PulseAudio is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published - by the Free Software Foundation; either version 2 of the License, + by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. PulseAudio is distributed in the hope that it will be useful, but @@ -29,17 +29,18 @@ #include #endif +#ifdef HAVE_SPEEX #include - -#include -#include +#endif #include #include #include #include #include - +#include +#include +#include #include "ffmpeg/avcodec.h" #include "resampler.h" @@ -53,65 +54,69 @@ struct pa_resampler { pa_sample_spec i_ss, o_ss; pa_channel_map i_cm, o_cm; - size_t i_fz, o_fz, w_sz; + size_t i_fz, o_fz, w_fz, w_sz; pa_mempool *mempool; - pa_memchunk buf1, buf2, buf3, buf4; - unsigned buf1_samples, buf2_samples, buf3_samples, buf4_samples; + pa_memchunk to_work_format_buf; + pa_memchunk remap_buf; + pa_memchunk resample_buf; + pa_memchunk from_work_format_buf; + size_t to_work_format_buf_size; + size_t remap_buf_size; + size_t resample_buf_size; + size_t from_work_format_buf_size; + + /* points to buffer before resampling stage, remap or to_work */ + pa_memchunk *leftover_buf; + size_t *leftover_buf_size; + + /* have_leftover points to leftover_in_remap or leftover_in_to_work */ + bool *have_leftover; + bool leftover_in_remap; + bool leftover_in_to_work; pa_sample_format_t work_format; + uint8_t work_channels; pa_convert_func_t to_work_format_func; pa_convert_func_t from_work_format_func; - float map_table[PA_CHANNELS_MAX][PA_CHANNELS_MAX]; - pa_bool_t map_required; - - void (*impl_free)(pa_resampler *r); - void (*impl_update_rates)(pa_resampler *r); - void (*impl_resample)(pa_resampler *r, const pa_memchunk *in, unsigned in_samples, pa_memchunk *out, unsigned *out_samples); - void (*impl_reset)(pa_resampler *r); + pa_remap_t remap; + bool map_required; - struct { /* data specific to the trivial resampler */ - unsigned o_counter; - unsigned i_counter; - } trivial; - - struct { /* data specific to the peak finder pseudo resampler */ - unsigned o_counter; - unsigned i_counter; - - float max_f[PA_CHANNELS_MAX]; - int16_t max_i[PA_CHANNELS_MAX]; + pa_resampler_impl impl; +}; - } peaks; +struct trivial_data { /* data specific to the trivial resampler */ + unsigned o_counter; + unsigned i_counter; +}; -#ifdef HAVE_LIBSAMPLERATE - struct { /* data specific to libsamplerate */ - SRC_STATE *state; - } src; -#endif +struct peaks_data { /* data specific to the peak finder pseudo resampler */ + unsigned o_counter; + unsigned i_counter; - struct { /* data specific to speex */ - SpeexResamplerState* state; - } speex; + float max_f[PA_CHANNELS_MAX]; + int16_t max_i[PA_CHANNELS_MAX]; +}; - struct { /* data specific to ffmpeg */ - struct AVResampleContext *state; - pa_memchunk buf[PA_CHANNELS_MAX]; - } ffmpeg; +struct ffmpeg_data { /* data specific to ffmpeg */ + struct AVResampleContext *state; }; static int copy_init(pa_resampler *r); static int trivial_init(pa_resampler*r); +#ifdef HAVE_SPEEX static int speex_init(pa_resampler*r); +#endif static int ffmpeg_init(pa_resampler*r); static int peaks_init(pa_resampler*r); #ifdef HAVE_LIBSAMPLERATE static int libsamplerate_init(pa_resampler*r); #endif -static void calc_map_table(pa_resampler *r); +static void setup_remap(const pa_resampler *r, pa_remap_t *m); +static void free_remap(pa_remap_t *m); static int (* const init_table[])(pa_resampler*r) = { #ifdef HAVE_LIBSAMPLERATE @@ -128,6 +133,7 @@ static int (* const init_table[])(pa_resampler*r) = { [PA_RESAMPLER_SRC_LINEAR] = NULL, #endif [PA_RESAMPLER_TRIVIAL] = trivial_init, +#ifdef HAVE_SPEEX [PA_RESAMPLER_SPEEX_FLOAT_BASE+0] = speex_init, [PA_RESAMPLER_SPEEX_FLOAT_BASE+1] = speex_init, [PA_RESAMPLER_SPEEX_FLOAT_BASE+2] = speex_init, @@ -150,12 +156,216 @@ static int (* const init_table[])(pa_resampler*r) = { [PA_RESAMPLER_SPEEX_FIXED_BASE+8] = speex_init, [PA_RESAMPLER_SPEEX_FIXED_BASE+9] = speex_init, [PA_RESAMPLER_SPEEX_FIXED_BASE+10] = speex_init, +#else + [PA_RESAMPLER_SPEEX_FLOAT_BASE+0] = NULL, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+1] = NULL, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+2] = NULL, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+3] = NULL, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+4] = NULL, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+5] = NULL, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+6] = NULL, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+7] = NULL, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+8] = NULL, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+9] = NULL, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+10] = NULL, + [PA_RESAMPLER_SPEEX_FIXED_BASE+0] = NULL, + [PA_RESAMPLER_SPEEX_FIXED_BASE+1] = NULL, + [PA_RESAMPLER_SPEEX_FIXED_BASE+2] = NULL, + [PA_RESAMPLER_SPEEX_FIXED_BASE+3] = NULL, + [PA_RESAMPLER_SPEEX_FIXED_BASE+4] = NULL, + [PA_RESAMPLER_SPEEX_FIXED_BASE+5] = NULL, + [PA_RESAMPLER_SPEEX_FIXED_BASE+6] = NULL, + [PA_RESAMPLER_SPEEX_FIXED_BASE+7] = NULL, + [PA_RESAMPLER_SPEEX_FIXED_BASE+8] = NULL, + [PA_RESAMPLER_SPEEX_FIXED_BASE+9] = NULL, + [PA_RESAMPLER_SPEEX_FIXED_BASE+10] = NULL, +#endif [PA_RESAMPLER_FFMPEG] = ffmpeg_init, [PA_RESAMPLER_AUTO] = NULL, [PA_RESAMPLER_COPY] = copy_init, [PA_RESAMPLER_PEAKS] = peaks_init, }; +static bool speex_is_fixed_point(void); + +static pa_resample_method_t choose_auto_resampler(pa_resample_flags_t flags) { + pa_resample_method_t method; + + if (pa_resample_method_supported(PA_RESAMPLER_SPEEX_FLOAT_BASE + 1)) + method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1; + else if (flags & PA_RESAMPLER_VARIABLE_RATE) + method = PA_RESAMPLER_TRIVIAL; + else + method = PA_RESAMPLER_FFMPEG; + + return method; +} + +static pa_resample_method_t fix_method( + pa_resample_flags_t flags, + pa_resample_method_t method, + const uint32_t rate_a, + const uint32_t rate_b) { + + pa_assert(pa_sample_rate_valid(rate_a)); + pa_assert(pa_sample_rate_valid(rate_b)); + pa_assert(method >= 0); + pa_assert(method < PA_RESAMPLER_MAX); + + if (!(flags & PA_RESAMPLER_VARIABLE_RATE) && rate_a == rate_b) { + pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates."); + method = PA_RESAMPLER_COPY; + } + + if (!pa_resample_method_supported(method)) { + pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(method)); + method = PA_RESAMPLER_AUTO; + } + + switch (method) { + case PA_RESAMPLER_COPY: + if (rate_a != rate_b) { + pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'."); + method = PA_RESAMPLER_AUTO; + break; + } + /* Else fall through */ + case PA_RESAMPLER_FFMPEG: + if (flags & PA_RESAMPLER_VARIABLE_RATE) { + pa_log_info("Resampler '%s' cannot do variable rate, reverting to resampler 'auto'.", pa_resample_method_to_string(method)); + method = PA_RESAMPLER_AUTO; + } + break; + + /* The Peaks resampler only supports downsampling. + * Revert to auto if we are upsampling */ + case PA_RESAMPLER_PEAKS: + if (rate_a < rate_b) { + pa_log_warn("The 'peaks' resampler only supports downsampling, reverting to resampler 'auto'."); + method = PA_RESAMPLER_AUTO; + } + break; + + default: + break; + } + + if (method == PA_RESAMPLER_AUTO) + method = choose_auto_resampler(flags); + + /* At this point, method is supported in the sense that it + * has an init function and supports the required flags. However, + * speex-float implementation in PulseAudio relies on the + * assumption that is invalid if speex has been compiled with + * --enable-fixed-point. Besides, speex-fixed is more efficient + * in this configuration. So use it instead. + */ + if (method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && method <= PA_RESAMPLER_SPEEX_FLOAT_MAX) { + if (speex_is_fixed_point()) { + pa_log_info("Speex appears to be compiled with --enable-fixed-point. " + "Switching to a fixed-point resampler because it should be faster."); + method = method - PA_RESAMPLER_SPEEX_FLOAT_BASE + PA_RESAMPLER_SPEEX_FIXED_BASE; + } + } + return method; +} + +/* Return true if a is a more precise sample format than b, else return false */ +static bool sample_format_more_precise(pa_sample_format_t a, pa_sample_format_t b) { + pa_assert(pa_sample_format_valid(a)); + pa_assert(pa_sample_format_valid(b)); + + switch (a) { + case PA_SAMPLE_U8: + case PA_SAMPLE_ALAW: + case PA_SAMPLE_ULAW: + return false; + break; + + case PA_SAMPLE_S16LE: + case PA_SAMPLE_S16BE: + if (b == PA_SAMPLE_ULAW || b == PA_SAMPLE_ALAW || b == PA_SAMPLE_U8) + return true; + else + return false; + break; + + case PA_SAMPLE_S24LE: + case PA_SAMPLE_S24BE: + case PA_SAMPLE_S24_32LE: + case PA_SAMPLE_S24_32BE: + if (b == PA_SAMPLE_ULAW || b == PA_SAMPLE_ALAW || b == PA_SAMPLE_U8 || + b == PA_SAMPLE_S16LE || b == PA_SAMPLE_S16BE) + return true; + else + return false; + break; + + case PA_SAMPLE_FLOAT32LE: + case PA_SAMPLE_FLOAT32BE: + case PA_SAMPLE_S32LE: + case PA_SAMPLE_S32BE: + if (b == PA_SAMPLE_FLOAT32LE || b == PA_SAMPLE_FLOAT32BE || + b == PA_SAMPLE_S32LE || b == PA_SAMPLE_FLOAT32BE) + return false; + else + return true; + break; + + default: + return false; + } +} + +static pa_sample_format_t choose_work_format( + pa_resample_method_t method, + pa_sample_format_t a, + pa_sample_format_t b, + bool map_required) { + pa_sample_format_t work_format; + + pa_assert(pa_sample_format_valid(a)); + pa_assert(pa_sample_format_valid(b)); + pa_assert(method >= 0); + pa_assert(method < PA_RESAMPLER_MAX); + + if (method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX) + method = PA_RESAMPLER_SPEEX_FIXED_BASE; + + switch (method) { + /* This block is for resampling functions that only + * support the S16 sample format. */ + case PA_RESAMPLER_SPEEX_FIXED_BASE: /* fall through */ + case PA_RESAMPLER_FFMPEG: + work_format = PA_SAMPLE_S16NE; + break; + + /* This block is for resampling functions that support + * any sample format. */ + case PA_RESAMPLER_COPY: /* fall through */ + case PA_RESAMPLER_TRIVIAL: + if (!map_required && a == b) { + work_format = a; + break; + } + /* Else fall trough */ + case PA_RESAMPLER_PEAKS: + if (a == PA_SAMPLE_S16NE || b == PA_SAMPLE_S16NE) + work_format = PA_SAMPLE_S16NE; + else if (sample_format_more_precise(a, PA_SAMPLE_S16NE) || + sample_format_more_precise(b, PA_SAMPLE_S16NE)) + work_format = PA_SAMPLE_FLOAT32NE; + else + work_format = PA_SAMPLE_S16NE; + break; + + default: + work_format = PA_SAMPLE_FLOAT32NE; + } + + return work_format; +} + pa_resampler* pa_resampler_new( pa_mempool *pool, const pa_sample_spec *a, @@ -175,41 +385,13 @@ pa_resampler* pa_resampler_new( pa_assert(method >= 0); pa_assert(method < PA_RESAMPLER_MAX); - /* Fix method */ - - if (!(flags & PA_RESAMPLER_VARIABLE_RATE) && a->rate == b->rate) { - pa_log_info("Forcing resampler 'copy', because of fixed, identical sample rates."); - method = PA_RESAMPLER_COPY; - } - - if (!pa_resample_method_supported(method)) { - pa_log_warn("Support for resampler '%s' not compiled in, reverting to 'auto'.", pa_resample_method_to_string(method)); - method = PA_RESAMPLER_AUTO; - } - - if (method == PA_RESAMPLER_FFMPEG && (flags & PA_RESAMPLER_VARIABLE_RATE)) { - pa_log_info("Resampler 'ffmpeg' cannot do variable rate, reverting to resampler 'auto'."); - method = PA_RESAMPLER_AUTO; - } - - if (method == PA_RESAMPLER_COPY && ((flags & PA_RESAMPLER_VARIABLE_RATE) || a->rate != b->rate)) { - pa_log_info("Resampler 'copy' cannot change sampling rate, reverting to resampler 'auto'."); - method = PA_RESAMPLER_AUTO; - } + method = fix_method(flags, method, a->rate, b->rate); - if (method == PA_RESAMPLER_AUTO) - method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 3; - - r = pa_xnew(pa_resampler, 1); + r = pa_xnew0(pa_resampler, 1); r->mempool = pool; r->method = method; r->flags = flags; - r->impl_free = NULL; - r->impl_update_rates = NULL; - r->impl_resample = NULL; - r->impl_reset = NULL; - /* Fill sample specs */ r->i_ss = *a; r->o_ss = *b; @@ -227,67 +409,62 @@ pa_resampler* pa_resampler_new( r->i_fz = pa_frame_size(a); r->o_fz = pa_frame_size(b); - pa_memchunk_reset(&r->buf1); - pa_memchunk_reset(&r->buf2); - pa_memchunk_reset(&r->buf3); - pa_memchunk_reset(&r->buf4); - - r->buf1_samples = r->buf2_samples = r->buf3_samples = r->buf4_samples = 0; - - calc_map_table(r); - - pa_log_info("Using resampler '%s'", pa_resample_method_to_string(method)); + r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) && + !pa_channel_map_equal(&r->i_cm, &r->o_cm))); - if ((method >= PA_RESAMPLER_SPEEX_FIXED_BASE && method <= PA_RESAMPLER_SPEEX_FIXED_MAX) || - (method == PA_RESAMPLER_FFMPEG)) - r->work_format = PA_SAMPLE_S16NE; - else if (method == PA_RESAMPLER_TRIVIAL || method == PA_RESAMPLER_COPY || method == PA_RESAMPLER_PEAKS) { - - if (r->map_required || a->format != b->format || method == PA_RESAMPLER_PEAKS) { - - if (a->format == PA_SAMPLE_S32NE || a->format == PA_SAMPLE_S32RE || - a->format == PA_SAMPLE_FLOAT32NE || a->format == PA_SAMPLE_FLOAT32RE || - a->format == PA_SAMPLE_S24NE || a->format == PA_SAMPLE_S24RE || - a->format == PA_SAMPLE_S24_32NE || a->format == PA_SAMPLE_S24_32RE || - b->format == PA_SAMPLE_S32NE || b->format == PA_SAMPLE_S32RE || - b->format == PA_SAMPLE_FLOAT32NE || b->format == PA_SAMPLE_FLOAT32RE || - b->format == PA_SAMPLE_S24NE || b->format == PA_SAMPLE_S24RE || - b->format == PA_SAMPLE_S24_32NE || b->format == PA_SAMPLE_S24_32RE) - r->work_format = PA_SAMPLE_FLOAT32NE; - else - r->work_format = PA_SAMPLE_S16NE; - - } else - r->work_format = a->format; + r->work_format = choose_work_format(method, a->format, b->format, r->map_required); + r->w_sz = pa_sample_size_of_format(r->work_format); - } else - r->work_format = PA_SAMPLE_FLOAT32NE; + if (r->i_ss.format != r->work_format) { + if (r->work_format == PA_SAMPLE_FLOAT32NE) { + if (!(r->to_work_format_func = pa_get_convert_to_float32ne_function(r->i_ss.format))) + goto fail; + } else { + pa_assert(r->work_format == PA_SAMPLE_S16NE); + if (!(r->to_work_format_func = pa_get_convert_to_s16ne_function(r->i_ss.format))) + goto fail; + } + } - pa_log_info("Using %s as working format.", pa_sample_format_to_string(r->work_format)); + if (r->o_ss.format != r->work_format) { + if (r->work_format == PA_SAMPLE_FLOAT32NE) { + if (!(r->from_work_format_func = pa_get_convert_from_float32ne_function(r->o_ss.format))) + goto fail; + } else { + pa_assert(r->work_format == PA_SAMPLE_S16NE); + if (!(r->from_work_format_func = pa_get_convert_from_s16ne_function(r->o_ss.format))) + goto fail; + } + } - r->w_sz = pa_sample_size_of_format(r->work_format); + if (r->o_ss.channels <= r->i_ss.channels) { + /* pipeline is: format conv. -> remap -> resample -> format conv. */ + r->work_channels = r->o_ss.channels; - if (r->i_ss.format == r->work_format) - r->to_work_format_func = NULL; - else if (r->work_format == PA_SAMPLE_FLOAT32NE) { - if (!(r->to_work_format_func = pa_get_convert_to_float32ne_function(r->i_ss.format))) - goto fail; + /* leftover buffer is remap output buffer (before resampling) */ + r->leftover_buf = &r->remap_buf; + r->leftover_buf_size = &r->remap_buf_size; + r->have_leftover = &r->leftover_in_remap; } else { - pa_assert(r->work_format == PA_SAMPLE_S16NE); - if (!(r->to_work_format_func = pa_get_convert_to_s16ne_function(r->i_ss.format))) - goto fail; - } + /* pipeline is: format conv. -> resample -> remap -> format conv. */ + r->work_channels = r->i_ss.channels; - if (r->o_ss.format == r->work_format) - r->from_work_format_func = NULL; - else if (r->work_format == PA_SAMPLE_FLOAT32NE) { - if (!(r->from_work_format_func = pa_get_convert_from_float32ne_function(r->o_ss.format))) - goto fail; - } else { - pa_assert(r->work_format == PA_SAMPLE_S16NE); - if (!(r->from_work_format_func = pa_get_convert_from_s16ne_function(r->o_ss.format))) - goto fail; + /* leftover buffer is to_work output buffer (before resampling) */ + r->leftover_buf = &r->to_work_format_buf; + r->leftover_buf_size = &r->to_work_format_buf_size; + r->have_leftover = &r->leftover_in_to_work; } + r->w_fz = pa_sample_size_of_format(r->work_format) * r->work_channels; + + pa_log_debug("Resampler:"); + pa_log_debug(" rate %d -> %d (method %s)", a->rate, b->rate, pa_resample_method_to_string(r->method)); + pa_log_debug(" format %s -> %s (intermediate %s)", pa_sample_format_to_string(a->format), + pa_sample_format_to_string(b->format), pa_sample_format_to_string(r->work_format)); + pa_log_debug(" channels %d -> %d (resampling %d)", a->channels, b->channels, r->work_channels); + + /* set up the remap structure */ + if (r->map_required) + setup_remap(r, &r->remap); /* initialize implementation */ if (init_table[method](r) < 0) @@ -296,8 +473,7 @@ pa_resampler* pa_resampler_new( return r; fail: - if (r) - pa_xfree(r); + pa_xfree(r); return NULL; } @@ -305,17 +481,21 @@ fail: void pa_resampler_free(pa_resampler *r) { pa_assert(r); - if (r->impl_free) - r->impl_free(r); + if (r->impl.free) + r->impl.free(r); + else + pa_xfree(r->impl.data); + + if (r->to_work_format_buf.memblock) + pa_memblock_unref(r->to_work_format_buf.memblock); + if (r->remap_buf.memblock) + pa_memblock_unref(r->remap_buf.memblock); + if (r->resample_buf.memblock) + pa_memblock_unref(r->resample_buf.memblock); + if (r->from_work_format_buf.memblock) + pa_memblock_unref(r->from_work_format_buf.memblock); - if (r->buf1.memblock) - pa_memblock_unref(r->buf1.memblock); - if (r->buf2.memblock) - pa_memblock_unref(r->buf2.memblock); - if (r->buf3.memblock) - pa_memblock_unref(r->buf3.memblock); - if (r->buf4.memblock) - pa_memblock_unref(r->buf4.memblock); + free_remap(&r->remap); pa_xfree(r); } @@ -323,43 +503,64 @@ void pa_resampler_free(pa_resampler *r) { void pa_resampler_set_input_rate(pa_resampler *r, uint32_t rate) { pa_assert(r); pa_assert(rate > 0); + pa_assert(r->impl.update_rates); if (r->i_ss.rate == rate) return; r->i_ss.rate = rate; - r->impl_update_rates(r); + r->impl.update_rates(r); } void pa_resampler_set_output_rate(pa_resampler *r, uint32_t rate) { pa_assert(r); pa_assert(rate > 0); + pa_assert(r->impl.update_rates); if (r->o_ss.rate == rate) return; r->o_ss.rate = rate; - r->impl_update_rates(r); + r->impl.update_rates(r); } size_t pa_resampler_request(pa_resampler *r, size_t out_length) { pa_assert(r); - return (((out_length / r->o_fz)*r->i_ss.rate)/r->o_ss.rate) * r->i_fz; + /* Let's round up here to make it more likely that the caller will get at + * least out_length amount of data from pa_resampler_run(). + * + * We don't take the leftover into account here. If we did, then it might + * be in theory possible that this function would return 0 and + * pa_resampler_run() would also return 0. That could lead to infinite + * loops. When the leftover is ignored here, such loops would eventually + * terminate, because the leftover would grow each round, finally + * surpassing the minimum input threshold of the resampler. */ + return ((((uint64_t) ((out_length + r->o_fz-1) / r->o_fz) * r->i_ss.rate) + r->o_ss.rate-1) / r->o_ss.rate) * r->i_fz; } size_t pa_resampler_result(pa_resampler *r, size_t in_length) { + size_t frames; + pa_assert(r); - return (((in_length / r->i_fz)*r->o_ss.rate)/r->i_ss.rate) * r->o_fz; + /* Let's round up here to ensure that the caller will always allocate big + * enough output buffer. */ + + frames = (in_length + r->i_fz - 1) / r->i_fz; + if (*r->have_leftover) + frames += r->leftover_buf->length / r->w_fz; + + return (((uint64_t) frames * r->o_ss.rate + r->i_ss.rate - 1) / r->i_ss.rate) * r->o_fz; } size_t pa_resampler_max_block_size(pa_resampler *r) { size_t block_size_max; - pa_sample_spec ss; - size_t fs; + pa_sample_spec max_ss; + size_t max_fs; + size_t frames; pa_assert(r); @@ -367,24 +568,47 @@ size_t pa_resampler_max_block_size(pa_resampler *r) { /* We deduce the "largest" sample spec we're using during the * conversion */ - ss.channels = (uint8_t) (PA_MAX(r->i_ss.channels, r->o_ss.channels)); + max_ss.channels = (uint8_t) (PA_MAX(r->i_ss.channels, r->o_ss.channels)); /* We silently assume that the format enum is ordered by size */ - ss.format = PA_MAX(r->i_ss.format, r->o_ss.format); - ss.format = PA_MAX(ss.format, r->work_format); - - ss.rate = PA_MAX(r->i_ss.rate, r->o_ss.rate); - - fs = pa_frame_size(&ss); - - return (((block_size_max/fs - EXTRA_FRAMES)*r->i_ss.rate)/ss.rate)*r->i_fz; + max_ss.format = PA_MAX(r->i_ss.format, r->o_ss.format); + max_ss.format = PA_MAX(max_ss.format, r->work_format); + + max_ss.rate = PA_MAX(r->i_ss.rate, r->o_ss.rate); + + max_fs = pa_frame_size(&max_ss); + frames = block_size_max / max_fs - EXTRA_FRAMES; + + pa_assert(frames >= (r->leftover_buf->length / r->w_fz)); + if (*r->have_leftover) + frames -= r->leftover_buf->length / r->w_fz; + + block_size_max = ((uint64_t) frames * r->i_ss.rate / max_ss.rate) * r->i_fz; + + if (block_size_max > 0) + return block_size_max; + else + /* A single input frame may result in so much output that it doesn't + * fit in one standard memblock (e.g. converting 1 Hz to 44100 Hz). In + * this case the max block size will be set to one frame, and some + * memory will be probably be allocated with malloc() instead of using + * the memory pool. + * + * XXX: Should we support this case at all? We could also refuse to + * create resamplers whose max block size would exceed the memory pool + * block size. In this case also updating the resampler rate should + * fail if the new rate would cause an excessive max block size (in + * which case the stream would probably have to be killed). */ + return r->i_fz; } void pa_resampler_reset(pa_resampler *r) { pa_assert(r); - if (r->impl_reset) - r->impl_reset(r); + if (r->impl.reset) + r->impl.reset(r); + + *r->have_leftover = false; } pa_resample_method_t pa_resampler_get_method(pa_resampler *r) { @@ -470,6 +694,13 @@ int pa_resample_method_supported(pa_resample_method_t m) { return 0; #endif +#ifndef HAVE_SPEEX + if (m >= PA_RESAMPLER_SPEEX_FLOAT_BASE && m <= PA_RESAMPLER_SPEEX_FLOAT_MAX) + return 0; + if (m >= PA_RESAMPLER_SPEEX_FIXED_BASE && m <= PA_RESAMPLER_SPEEX_FIXED_MAX) + return 0; +#endif + return 1; } @@ -479,19 +710,19 @@ pa_resample_method_t pa_parse_resample_method(const char *string) { pa_assert(string); for (m = 0; m < PA_RESAMPLER_MAX; m++) - if (!strcmp(string, resample_methods[m])) + if (pa_streq(string, resample_methods[m])) return m; - if (!strcmp(string, "speex-fixed")) - return PA_RESAMPLER_SPEEX_FIXED_BASE + 3; + if (pa_streq(string, "speex-fixed")) + return PA_RESAMPLER_SPEEX_FIXED_BASE + 1; - if (!strcmp(string, "speex-float")) - return PA_RESAMPLER_SPEEX_FLOAT_BASE + 3; + if (pa_streq(string, "speex-float")) + return PA_RESAMPLER_SPEEX_FLOAT_BASE + 1; return PA_RESAMPLER_INVALID; } -static pa_bool_t on_left(pa_channel_position_t p) { +static bool on_left(pa_channel_position_t p) { return p == PA_CHANNEL_POSITION_FRONT_LEFT || @@ -502,7 +733,7 @@ static pa_bool_t on_left(pa_channel_position_t p) { p == PA_CHANNEL_POSITION_TOP_REAR_LEFT; } -static pa_bool_t on_right(pa_channel_position_t p) { +static bool on_right(pa_channel_position_t p) { return p == PA_CHANNEL_POSITION_FRONT_RIGHT || @@ -513,7 +744,7 @@ static pa_bool_t on_right(pa_channel_position_t p) { p == PA_CHANNEL_POSITION_TOP_REAR_RIGHT; } -static pa_bool_t on_center(pa_channel_position_t p) { +static bool on_center(pa_channel_position_t p) { return p == PA_CHANNEL_POSITION_FRONT_CENTER || @@ -523,12 +754,12 @@ static pa_bool_t on_center(pa_channel_position_t p) { p == PA_CHANNEL_POSITION_TOP_REAR_CENTER; } -static pa_bool_t on_lfe(pa_channel_position_t p) { +static bool on_lfe(pa_channel_position_t p) { return p == PA_CHANNEL_POSITION_LFE; } -static pa_bool_t on_front(pa_channel_position_t p) { +static bool on_front(pa_channel_position_t p) { return p == PA_CHANNEL_POSITION_FRONT_LEFT || p == PA_CHANNEL_POSITION_FRONT_RIGHT || @@ -540,7 +771,7 @@ static pa_bool_t on_front(pa_channel_position_t p) { p == PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER; } -static pa_bool_t on_rear(pa_channel_position_t p) { +static bool on_rear(pa_channel_position_t p) { return p == PA_CHANNEL_POSITION_REAR_LEFT || p == PA_CHANNEL_POSITION_REAR_RIGHT || @@ -550,7 +781,7 @@ static pa_bool_t on_rear(pa_channel_position_t p) { p == PA_CHANNEL_POSITION_TOP_REAR_CENTER; } -static pa_bool_t on_side(pa_channel_position_t p) { +static bool on_side(pa_channel_position_t p) { return p == PA_CHANNEL_POSITION_SIDE_LEFT || p == PA_CHANNEL_POSITION_SIDE_RIGHT || @@ -574,237 +805,232 @@ static int front_rear_side(pa_channel_position_t p) { return ON_OTHER; } -static void calc_map_table(pa_resampler *r) { +static void setup_remap(const pa_resampler *r, pa_remap_t *m) { unsigned oc, ic; - pa_bool_t ic_connected[PA_CHANNELS_MAX]; - pa_bool_t remix; + unsigned n_oc, n_ic; + bool ic_connected[PA_CHANNELS_MAX]; + bool remix; pa_strbuf *s; char *t; pa_assert(r); + pa_assert(m); - if (!(r->map_required = (r->i_ss.channels != r->o_ss.channels || (!(r->flags & PA_RESAMPLER_NO_REMAP) && !pa_channel_map_equal(&r->i_cm, &r->o_cm))))) - return; + n_oc = r->o_ss.channels; + n_ic = r->i_ss.channels; + + m->format = r->work_format; + m->i_ss = r->i_ss; + m->o_ss = r->o_ss; + + memset(m->map_table_f, 0, sizeof(m->map_table_f)); + memset(m->map_table_i, 0, sizeof(m->map_table_i)); - memset(r->map_table, 0, sizeof(r->map_table)); memset(ic_connected, 0, sizeof(ic_connected)); - remix = (r->flags & (PA_RESAMPLER_NO_REMAP|PA_RESAMPLER_NO_REMIX)) == 0; + remix = (r->flags & (PA_RESAMPLER_NO_REMAP | PA_RESAMPLER_NO_REMIX)) == 0; - for (oc = 0; oc < r->o_ss.channels; oc++) { - pa_bool_t oc_connected = FALSE; - pa_channel_position_t b = r->o_cm.map[oc]; + if (r->flags & PA_RESAMPLER_NO_REMAP) { + pa_assert(!remix); - for (ic = 0; ic < r->i_ss.channels; ic++) { - pa_channel_position_t a = r->i_cm.map[ic]; + for (oc = 0; oc < PA_MIN(n_ic, n_oc); oc++) + m->map_table_f[oc][oc] = 1.0f; - if (r->flags & PA_RESAMPLER_NO_REMAP) { - /* We shall not do any remapping. Hence, just check by index */ + } else if (r->flags & PA_RESAMPLER_NO_REMIX) { + pa_assert(!remix); + for (oc = 0; oc < n_oc; oc++) { + pa_channel_position_t b = r->o_cm.map[oc]; - if (ic == oc) - r->map_table[oc][ic] = 1.0; + for (ic = 0; ic < n_ic; ic++) { + pa_channel_position_t a = r->i_cm.map[ic]; - continue; + /* We shall not do any remixing. Hence, just check by name */ + if (a == b) + m->map_table_f[oc][ic] = 1.0f; } + } + } else { - if (r->flags & PA_RESAMPLER_NO_REMIX) { - /* We shall not do any remixing. Hence, just check by name */ + /* OK, we shall do the full monty: upmixing and downmixing. Our + * algorithm is relatively simple, does not do spacialization, delay + * elements or apply lowpass filters for LFE. Patches are always + * welcome, though. Oh, and it doesn't do any matrix decoding. (Which + * probably wouldn't make any sense anyway.) + * + * This code is not idempotent: downmixing an upmixed stereo stream is + * not identical to the original. The volume will not match, and the + * two channels will be a linear combination of both. + * + * This is loosely based on random suggestions found on the Internet, + * such as this: + * http://www.halfgaar.net/surround-sound-in-linux and the alsa upmix + * plugin. + * + * The algorithm works basically like this: + * + * 1) Connect all channels with matching names. + * + * 2) Mono Handling: + * S:Mono: Copy into all D:channels + * D:Mono: Avg all S:channels + * + * 3) Mix D:Left, D:Right: + * D:Left: If not connected, avg all S:Left + * D:Right: If not connected, avg all S:Right + * + * 4) Mix D:Center + * If not connected, avg all S:Center + * If still not connected, avg all S:Left, S:Right + * + * 5) Mix D:LFE + * If not connected, avg all S:* + * + * 6) Make sure S:Left/S:Right is used: S:Left/S:Right: If not + * connected, mix into all D:left and all D:right channels. Gain is + * 1/9. + * + * 7) Make sure S:Center, S:LFE is used: + * + * S:Center, S:LFE: If not connected, mix into all D:left, all + * D:right, all D:center channels. Gain is 0.5 for center and 0.375 + * for LFE. C-front is only mixed into L-front/R-front if available, + * otherwise into all L/R channels. Similarly for C-rear. + * + * 8) Normalize each row in the matrix such that the sum for each row is + * not larger than 1.0 in order to avoid clipping. + * + * S: and D: shall relate to the source resp. destination channels. + * + * Rationale: 1, 2 are probably obvious. For 3: this copies front to + * rear if needed. For 4: we try to find some suitable C source for C, + * if we don't find any, we avg L and R. For 5: LFE is mixed from all + * channels. For 6: the rear channels should not be dropped entirely, + * however have only minimal impact. For 7: movies usually encode + * speech on the center channel. Thus we have to make sure this channel + * is distributed to L and R if not available in the output. Also, LFE + * is used to achieve a greater dynamic range, and thus we should try + * to do our best to pass it to L+R. + */ - if (a == b) - r->map_table[oc][ic] = 1.0; + unsigned + ic_left = 0, + ic_right = 0, + ic_center = 0, + ic_unconnected_left = 0, + ic_unconnected_right = 0, + ic_unconnected_center = 0, + ic_unconnected_lfe = 0; + bool ic_unconnected_center_mixed_in = 0; - continue; - } + pa_assert(remix); - pa_assert(remix); - - /* OK, we shall do the full monty: upmixing and - * downmixing. Our algorithm is relatively simple, does - * not do spacialization, delay elements or apply lowpass - * filters for LFE. Patches are always welcome, - * though. Oh, and it doesn't do any matrix - * decoding. (Which probably wouldn't make any sense - * anyway.) - * - * This code is not idempotent: downmixing an upmixed - * stereo stream is not identical to the original. The - * volume will not match, and the two channels will be a - * linear combination of both. - * - * This is losely based on random suggestions found on the - * Internet, such as this: - * http://www.halfgaar.net/surround-sound-in-linux and the - * alsa upmix plugin. - * - * The algorithm works basically like this: - * - * 1) Connect all channels with matching names. - * - * 2) Mono Handling: - * S:Mono: Copy into all D:channels - * D:Mono: Copy in all S:channels - * - * 3) Mix D:Left, D:Right: - * D:Left: If not connected, avg all S:Left - * D:Right: If not connected, avg all S:Right - * - * 4) Mix D:Center - * If not connected, avg all S:Center - * If still not connected, avg all S:Left, S:Right - * - * 5) Mix D:LFE - * If not connected, avg all S:* - * - * 6) Make sure S:Left/S:Right is used: S:Left/S:Right: If - * not connected, mix into all D:left and all D:right - * channels. Gain is 0.1, the current left and right - * should be multiplied by 0.9. - * - * 7) Make sure S:Center, S:LFE is used: - * - * S:Center, S:LFE: If not connected, mix into all - * D:left, all D:right, all D:center channels, gain is - * 0.375. The current (as result of 1..6) factors - * should be multiplied by 0.75. (Alt. suggestion: 0.25 - * vs. 0.5) If C-front is only mixed into - * L-front/R-front if available, otherwise into all L/R - * channels. Similarly for C-rear. - * - * S: and D: shall relate to the source resp. destination channels. - * - * Rationale: 1, 2 are probably obvious. For 3: this - * copies front to rear if needed. For 4: we try to find - * some suitable C source for C, if we don't find any, we - * avg L and R. For 5: LFE is mixed from all channels. For - * 6: the rear channels should not be dropped entirely, - * however have only minimal impact. For 7: movies usually - * encode speech on the center channel. Thus we have to - * make sure this channel is distributed to L and R if not - * available in the output. Also, LFE is used to achieve a - * greater dynamic range, and thus we should try to do our - * best to pass it to L+R. - */ - - if (a == b || a == PA_CHANNEL_POSITION_MONO || b == PA_CHANNEL_POSITION_MONO) { - r->map_table[oc][ic] = 1.0; - - oc_connected = TRUE; - ic_connected[ic] = TRUE; - } + for (ic = 0; ic < n_ic; ic++) { + if (on_left(r->i_cm.map[ic])) + ic_left++; + if (on_right(r->i_cm.map[ic])) + ic_right++; + if (on_center(r->i_cm.map[ic])) + ic_center++; } - if (!oc_connected && remix) { - /* OK, we shall remix */ + for (oc = 0; oc < n_oc; oc++) { + bool oc_connected = false; + pa_channel_position_t b = r->o_cm.map[oc]; - /* Try to find matching input ports for this output port */ + for (ic = 0; ic < n_ic; ic++) { + pa_channel_position_t a = r->i_cm.map[ic]; - if (on_left(b)) { - unsigned n = 0; + if (a == b || a == PA_CHANNEL_POSITION_MONO) { + m->map_table_f[oc][ic] = 1.0f; - /* We are not connected and on the left side, let's - * average all left side input channels. */ + oc_connected = true; + ic_connected[ic] = true; + } + else if (b == PA_CHANNEL_POSITION_MONO) { + m->map_table_f[oc][ic] = 1.0f / (float) n_ic; - for (ic = 0; ic < r->i_ss.channels; ic++) - if (on_left(r->i_cm.map[ic])) - n++; + oc_connected = true; + ic_connected[ic] = true; + } + } - if (n > 0) - for (ic = 0; ic < r->i_ss.channels; ic++) - if (on_left(r->i_cm.map[ic])) { - r->map_table[oc][ic] = 1.0f / (float) n; - ic_connected[ic] = TRUE; - } + if (!oc_connected) { + /* Try to find matching input ports for this output port */ - /* We ignore the case where there is no left input - * channel. Something is really wrong in this case - * anyway. */ + if (on_left(b)) { - } else if (on_right(b)) { - unsigned n = 0; + /* We are not connected and on the left side, let's + * average all left side input channels. */ - /* We are not connected and on the right side, let's - * average all right side input channels. */ + if (ic_left > 0) + for (ic = 0; ic < n_ic; ic++) + if (on_left(r->i_cm.map[ic])) { + m->map_table_f[oc][ic] = 1.0f / (float) ic_left; + ic_connected[ic] = true; + } - for (ic = 0; ic < r->i_ss.channels; ic++) - if (on_right(r->i_cm.map[ic])) - n++; + /* We ignore the case where there is no left input channel. + * Something is really wrong in this case anyway. */ - if (n > 0) - for (ic = 0; ic < r->i_ss.channels; ic++) - if (on_right(r->i_cm.map[ic])) { - r->map_table[oc][ic] = 1.0f / (float) n; - ic_connected[ic] = TRUE; - } + } else if (on_right(b)) { - /* We ignore the case where there is no right input - * channel. Something is really wrong in this case - * anyway. */ + /* We are not connected and on the right side, let's + * average all right side input channels. */ - } else if (on_center(b)) { - unsigned n = 0; + if (ic_right > 0) + for (ic = 0; ic < n_ic; ic++) + if (on_right(r->i_cm.map[ic])) { + m->map_table_f[oc][ic] = 1.0f / (float) ic_right; + ic_connected[ic] = true; + } + + /* We ignore the case where there is no right input + * channel. Something is really wrong in this case anyway. + * */ - /* We are not connected and at the center. Let's - * average all center input channels. */ + } else if (on_center(b)) { - for (ic = 0; ic < r->i_ss.channels; ic++) - if (on_center(r->i_cm.map[ic])) - n++; + if (ic_center > 0) { - if (n > 0) { - for (ic = 0; ic < r->i_ss.channels; ic++) - if (on_center(r->i_cm.map[ic])) { - r->map_table[oc][ic] = 1.0f / (float) n; - ic_connected[ic] = TRUE; - } - } else { + /* We are not connected and at the center. Let's average + * all center input channels. */ - /* Hmm, no center channel around, let's synthesize - * it by mixing L and R.*/ + for (ic = 0; ic < n_ic; ic++) + if (on_center(r->i_cm.map[ic])) { + m->map_table_f[oc][ic] = 1.0f / (float) ic_center; + ic_connected[ic] = true; + } - n = 0; + } else if (ic_left + ic_right > 0) { - for (ic = 0; ic < r->i_ss.channels; ic++) - if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) - n++; + /* Hmm, no center channel around, let's synthesize it + * by mixing L and R.*/ - if (n > 0) - for (ic = 0; ic < r->i_ss.channels; ic++) + for (ic = 0; ic < n_ic; ic++) if (on_left(r->i_cm.map[ic]) || on_right(r->i_cm.map[ic])) { - r->map_table[oc][ic] = 1.0f / (float) n; - ic_connected[ic] = TRUE; + m->map_table_f[oc][ic] = 1.0f / (float) (ic_left + ic_right); + ic_connected[ic] = true; } + } - /* We ignore the case where there is not even a - * left or right input channel. Something is - * really wrong in this case anyway. */ - } - - } else if (on_lfe(b)) { + /* We ignore the case where there is not even a left or + * right input channel. Something is really wrong in this + * case anyway. */ - /* We are not connected and an LFE. Let's average all - * channels for LFE. */ + } else if (on_lfe(b) && !(r->flags & PA_RESAMPLER_NO_LFE)) { - for (ic = 0; ic < r->i_ss.channels; ic++) { + /* We are not connected and an LFE. Let's average all + * channels for LFE. */ - if (!(r->flags & PA_RESAMPLER_NO_LFE)) - r->map_table[oc][ic] = 1.0f / (float) r->i_ss.channels; - else - r->map_table[oc][ic] = 0; + for (ic = 0; ic < n_ic; ic++) + m->map_table_f[oc][ic] = 1.0f / (float) n_ic; - /* Please note that a channel connected to LFE - * doesn't really count as connected. */ + /* Please note that a channel connected to LFE doesn't + * really count as connected. */ } } } - } - - if (remix) { - unsigned - ic_unconnected_left = 0, - ic_unconnected_right = 0, - ic_unconnected_center = 0, - ic_unconnected_lfe = 0; - for (ic = 0; ic < r->i_ss.channels; ic++) { + for (ic = 0; ic < n_ic; ic++) { pa_channel_position_t a = r->i_cm.map[ic]; if (ic_connected[ic]) @@ -820,396 +1046,305 @@ static void calc_map_table(pa_resampler *r) { ic_unconnected_lfe++; } - if (ic_unconnected_left > 0) { + for (ic = 0; ic < n_ic; ic++) { + pa_channel_position_t a = r->i_cm.map[ic]; - /* OK, so there are unconnected input channels on the - * left. Let's multiply all already connected channels on - * the left side by .9 and add in our averaged unconnected - * channels multplied by .1 */ + if (ic_connected[ic]) + continue; - for (oc = 0; oc < r->o_ss.channels; oc++) { + for (oc = 0; oc < n_oc; oc++) { + pa_channel_position_t b = r->o_cm.map[oc]; - if (!on_left(r->o_cm.map[oc])) - continue; + if (on_left(a) && on_left(b)) + m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_left; - for (ic = 0; ic < r->i_ss.channels; ic++) { + else if (on_right(a) && on_right(b)) + m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_right; - if (ic_connected[ic]) { - r->map_table[oc][ic] *= .9f; - continue; - } + else if (on_center(a) && on_center(b)) { + m->map_table_f[oc][ic] = (1.f/9.f) / (float) ic_unconnected_center; + ic_unconnected_center_mixed_in = true; - if (on_left(r->i_cm.map[ic])) - r->map_table[oc][ic] = .1f / (float) ic_unconnected_left; - } + } else if (on_lfe(a) && !(r->flags & PA_RESAMPLER_NO_LFE)) + m->map_table_f[oc][ic] = .375f / (float) ic_unconnected_lfe; } } - if (ic_unconnected_right > 0) { - - /* OK, so there are unconnected input channels on the - * right. Let's multiply all already connected channels on - * the right side by .9 and add in our averaged unconnected - * channels multplied by .1 */ + if (ic_unconnected_center > 0 && !ic_unconnected_center_mixed_in) { + unsigned ncenter[PA_CHANNELS_MAX]; + bool found_frs[PA_CHANNELS_MAX]; - for (oc = 0; oc < r->o_ss.channels; oc++) { + memset(ncenter, 0, sizeof(ncenter)); + memset(found_frs, 0, sizeof(found_frs)); - if (!on_right(r->o_cm.map[oc])) - continue; - - for (ic = 0; ic < r->i_ss.channels; ic++) { + /* Hmm, as it appears there was no center channel we + could mix our center channel in. In this case, mix it into + left and right. Using .5 as the factor. */ - if (ic_connected[ic]) { - r->map_table[oc][ic] *= .9f; - continue; - } + for (ic = 0; ic < n_ic; ic++) { - if (on_right(r->i_cm.map[ic])) - r->map_table[oc][ic] = .1f / (float) ic_unconnected_right; - } - } - } - - if (ic_unconnected_center > 0) { - pa_bool_t mixed_in = FALSE; - - /* OK, so there are unconnected input channels on the - * center. Let's multiply all already connected channels on - * the center side by .9 and add in our averaged unconnected - * channels multplied by .1 */ - - for (oc = 0; oc < r->o_ss.channels; oc++) { + if (ic_connected[ic]) + continue; - if (!on_center(r->o_cm.map[oc])) + if (!on_center(r->i_cm.map[ic])) continue; - for (ic = 0; ic < r->i_ss.channels; ic++) { + for (oc = 0; oc < n_oc; oc++) { - if (ic_connected[ic]) { - r->map_table[oc][ic] *= .9f; + if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc])) continue; - } - if (on_center(r->i_cm.map[ic])) { - r->map_table[oc][ic] = .1f / (float) ic_unconnected_center; - mixed_in = TRUE; + if (front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) { + found_frs[ic] = true; + break; } } - } - - if (!mixed_in) { - unsigned ncenter[PA_CHANNELS_MAX]; - pa_bool_t found_frs[PA_CHANNELS_MAX]; - - memset(ncenter, 0, sizeof(ncenter)); - memset(found_frs, 0, sizeof(found_frs)); - - /* Hmm, as it appears there was no center channel we - could mix our center channel in. In this case, mix - it into left and right. Using .375 and 0.75 as - factors. */ - for (ic = 0; ic < r->i_ss.channels; ic++) { + for (oc = 0; oc < n_oc; oc++) { - if (ic_connected[ic]) - continue; - - if (!on_center(r->i_cm.map[ic])) + if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc])) continue; - for (oc = 0; oc < r->o_ss.channels; oc++) { - - if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc])) - continue; - - if (front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) { - found_frs[ic] = TRUE; - break; - } - } - - for (oc = 0; oc < r->o_ss.channels; oc++) { - - if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc])) - continue; - - if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) - ncenter[oc]++; - } + if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) + ncenter[oc]++; } + } - for (oc = 0; oc < r->o_ss.channels; oc++) { - - if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc])) - continue; + for (oc = 0; oc < n_oc; oc++) { - if (ncenter[oc] <= 0) - continue; + if (!on_left(r->o_cm.map[oc]) && !on_right(r->o_cm.map[oc])) + continue; - for (ic = 0; ic < r->i_ss.channels; ic++) { + if (ncenter[oc] <= 0) + continue; - if (ic_connected[ic]) { - r->map_table[oc][ic] *= .75f; - continue; - } + for (ic = 0; ic < n_ic; ic++) { - if (!on_center(r->i_cm.map[ic])) - continue; + if (!on_center(r->i_cm.map[ic])) + continue; - if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) - r->map_table[oc][ic] = .375f / (float) ncenter[oc]; - } + if (!found_frs[ic] || front_rear_side(r->i_cm.map[ic]) == front_rear_side(r->o_cm.map[oc])) + m->map_table_f[oc][ic] = .5f / (float) ncenter[oc]; } } } + } - if (ic_unconnected_lfe > 0 && !(r->flags & PA_RESAMPLER_NO_LFE)) { - - /* OK, so there is an unconnected LFE channel. Let's mix - * it into all channels, with factor 0.375 */ - - for (ic = 0; ic < r->i_ss.channels; ic++) { - - if (!on_lfe(r->i_cm.map[ic])) - continue; + for (oc = 0; oc < n_oc; oc++) { + float sum = 0.0f; + for (ic = 0; ic < n_ic; ic++) + sum += m->map_table_f[oc][ic]; - for (oc = 0; oc < r->o_ss.channels; oc++) - r->map_table[oc][ic] = 0.375f / (float) ic_unconnected_lfe; - } - } + if (sum > 1.0f) + for (ic = 0; ic < n_ic; ic++) + m->map_table_f[oc][ic] /= sum; } + /* make an 16:16 int version of the matrix */ + for (oc = 0; oc < n_oc; oc++) + for (ic = 0; ic < n_ic; ic++) + m->map_table_i[oc][ic] = (int32_t) (m->map_table_f[oc][ic] * 0x10000); s = pa_strbuf_new(); pa_strbuf_printf(s, " "); - for (ic = 0; ic < r->i_ss.channels; ic++) + for (ic = 0; ic < n_ic; ic++) pa_strbuf_printf(s, " I%02u ", ic); pa_strbuf_puts(s, "\n +"); - for (ic = 0; ic < r->i_ss.channels; ic++) + for (ic = 0; ic < n_ic; ic++) pa_strbuf_printf(s, "------"); pa_strbuf_puts(s, "\n"); - for (oc = 0; oc < r->o_ss.channels; oc++) { + for (oc = 0; oc < n_oc; oc++) { pa_strbuf_printf(s, "O%02u |", oc); - for (ic = 0; ic < r->i_ss.channels; ic++) - pa_strbuf_printf(s, " %1.3f", r->map_table[oc][ic]); + for (ic = 0; ic < n_ic; ic++) + pa_strbuf_printf(s, " %1.3f", m->map_table_f[oc][ic]); pa_strbuf_puts(s, "\n"); } pa_log_debug("Channel matrix:\n%s", t = pa_strbuf_tostring_free(s)); pa_xfree(t); -} - -static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) { - unsigned n_samples; - void *src, *dst; - - pa_assert(r); - pa_assert(input); - pa_assert(input->memblock); - /* Convert the incoming sample into the work sample format and place them in buf1 */ + /* initialize the remapping function */ + pa_init_remap_func(m); +} - if (!r->to_work_format_func || !input->length) - return input; +static void free_remap(pa_remap_t *m) { + pa_assert(m); - n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels); + pa_xfree(m->state); +} - r->buf1.index = 0; - r->buf1.length = r->w_sz * n_samples; +/* check if buf's memblock is large enough to hold 'len' bytes; create a + * new memblock if necessary and optionally preserve 'copy' data bytes */ +static void fit_buf(pa_resampler *r, pa_memchunk *buf, size_t len, size_t *size, size_t copy) { + pa_assert(size); + + if (!buf->memblock || len > *size) { + pa_memblock *new_block = pa_memblock_new(r->mempool, len); + + if (buf->memblock) { + if (copy > 0) { + void *src = pa_memblock_acquire(buf->memblock); + void *dst = pa_memblock_acquire(new_block); + pa_assert(copy <= len); + memcpy(dst, src, copy); + pa_memblock_release(new_block); + pa_memblock_release(buf->memblock); + } - if (!r->buf1.memblock || r->buf1_samples < n_samples) { - if (r->buf1.memblock) - pa_memblock_unref(r->buf1.memblock); + pa_memblock_unref(buf->memblock); + } - r->buf1_samples = n_samples; - r->buf1.memblock = pa_memblock_new(r->mempool, r->buf1.length); + buf->memblock = new_block; + *size = len; } - src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index; - dst = (uint8_t*) pa_memblock_acquire(r->buf1.memblock); + buf->length = len; +} - r->to_work_format_func(n_samples, src, dst); +static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) { + unsigned in_n_samples, out_n_samples; + void *src, *dst; + bool have_leftover; + size_t leftover_length = 0; - pa_memblock_release(input->memblock); - pa_memblock_release(r->buf1.memblock); + pa_assert(r); + pa_assert(input); + pa_assert(input->memblock); - return &r->buf1; -} + /* Convert the incoming sample into the work sample format and place them + * in to_work_format_buf. The leftover data is already converted, so it's + * part of the output buffer. */ -static void vectoradd_s16_with_fraction( - int16_t *d, int dstr, - const int16_t *s1, int sstr1, - const int16_t *s2, int sstr2, - int n, - float s3, float s4) { + have_leftover = r->leftover_in_to_work; + r->leftover_in_to_work = false; - int32_t i3, i4; + if (!have_leftover && (!r->to_work_format_func || !input->length)) + return input; + else if (input->length <= 0) + return &r->to_work_format_buf; - i3 = (int32_t) (s3 * 0x10000); - i4 = (int32_t) (s4 * 0x10000); + in_n_samples = out_n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels); - for (; n > 0; n--) { - int32_t a, b; + if (have_leftover) { + leftover_length = r->to_work_format_buf.length; + out_n_samples += (unsigned) (leftover_length / r->w_sz); + } - a = *s1; - b = *s2; + fit_buf(r, &r->to_work_format_buf, r->w_sz * out_n_samples, &r->to_work_format_buf_size, leftover_length); - a = (a * i3) / 0x10000; - b = (b * i4) / 0x10000; + src = pa_memblock_acquire_chunk(input); + dst = (uint8_t *) pa_memblock_acquire(r->to_work_format_buf.memblock) + leftover_length; - *d = (int16_t) (a + b); + if (r->to_work_format_func) + r->to_work_format_func(in_n_samples, src, dst); + else + memcpy(dst, src, input->length); - s1 = (const int16_t*) ((const uint8_t*) s1 + sstr1); - s2 = (const int16_t*) ((const uint8_t*) s2 + sstr2); - d = (int16_t*) ((uint8_t*) d + dstr); + pa_memblock_release(input->memblock); + pa_memblock_release(r->to_work_format_buf.memblock); - } + return &r->to_work_format_buf; } static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *input) { - unsigned in_n_samples, out_n_samples, n_frames; - int i_skip, o_skip; - unsigned oc; + unsigned in_n_samples, out_n_samples, in_n_frames, out_n_frames; void *src, *dst; + size_t leftover_length = 0; + bool have_leftover; pa_assert(r); pa_assert(input); pa_assert(input->memblock); - /* Remap channels and place the result int buf2 */ + /* Remap channels and place the result in remap_buf. There may be leftover + * data in the beginning of remap_buf. The leftover data is already + * remapped, so it's not part of the input, it's part of the output. */ + + have_leftover = r->leftover_in_remap; + r->leftover_in_remap = false; - if (!r->map_required || !input->length) + if (!have_leftover && (!r->map_required || input->length <= 0)) return input; + else if (input->length <= 0) + return &r->remap_buf; in_n_samples = (unsigned) (input->length / r->w_sz); - n_frames = in_n_samples / r->i_ss.channels; - out_n_samples = n_frames * r->o_ss.channels; - - r->buf2.index = 0; - r->buf2.length = r->w_sz * out_n_samples; + in_n_frames = out_n_frames = in_n_samples / r->i_ss.channels; - if (!r->buf2.memblock || r->buf2_samples < out_n_samples) { - if (r->buf2.memblock) - pa_memblock_unref(r->buf2.memblock); - - r->buf2_samples = out_n_samples; - r->buf2.memblock = pa_memblock_new(r->mempool, r->buf2.length); + if (have_leftover) { + leftover_length = r->remap_buf.length; + out_n_frames += leftover_length / r->w_fz; } - src = ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index); - dst = pa_memblock_acquire(r->buf2.memblock); - - memset(dst, 0, r->buf2.length); - - o_skip = (int) (r->w_sz * r->o_ss.channels); - i_skip = (int) (r->w_sz * r->i_ss.channels); - - switch (r->work_format) { - case PA_SAMPLE_FLOAT32NE: - - for (oc = 0; oc < r->o_ss.channels; oc++) { - unsigned ic; - static const float one = 1.0; - - for (ic = 0; ic < r->i_ss.channels; ic++) { - - if (r->map_table[oc][ic] <= 0.0) - continue; - - oil_vectoradd_f32( - (float*) dst + oc, o_skip, - (float*) dst + oc, o_skip, - (float*) src + ic, i_skip, - (int) n_frames, - &one, &r->map_table[oc][ic]); - } - } - - break; - - case PA_SAMPLE_S16NE: - - for (oc = 0; oc < r->o_ss.channels; oc++) { - unsigned ic; + out_n_samples = out_n_frames * r->o_ss.channels; + fit_buf(r, &r->remap_buf, out_n_samples * r->w_sz, &r->remap_buf_size, leftover_length); - for (ic = 0; ic < r->i_ss.channels; ic++) { + src = pa_memblock_acquire_chunk(input); + dst = (uint8_t *) pa_memblock_acquire(r->remap_buf.memblock) + leftover_length; - if (r->map_table[oc][ic] <= 0.0) - continue; + if (r->map_required) { + pa_remap_t *remap = &r->remap; - if (r->map_table[oc][ic] >= 1.0) { - static const int16_t one = 1; + pa_assert(remap->do_remap); + remap->do_remap(remap, dst, src, in_n_frames); - oil_vectoradd_s16( - (int16_t*) dst + oc, o_skip, - (int16_t*) dst + oc, o_skip, - (int16_t*) src + ic, i_skip, - (int) n_frames, - &one, &one); + } else + memcpy(dst, src, input->length); - } else + pa_memblock_release(input->memblock); + pa_memblock_release(r->remap_buf.memblock); - vectoradd_s16_with_fraction( - (int16_t*) dst + oc, o_skip, - (int16_t*) dst + oc, o_skip, - (int16_t*) src + ic, i_skip, - (int) n_frames, - 1.0f, r->map_table[oc][ic]); - } - } + return &r->remap_buf; +} - break; +static void save_leftover(pa_resampler *r, void *buf, size_t len) { + void *dst; - default: - pa_assert_not_reached(); - } - - pa_memblock_release(input->memblock); - pa_memblock_release(r->buf2.memblock); + pa_assert(r); + pa_assert(buf); + pa_assert(len > 0); - r->buf2.length = out_n_samples * r->w_sz; + /* Store the leftover data. */ + fit_buf(r, r->leftover_buf, len, r->leftover_buf_size, 0); + *r->have_leftover = true; - return &r->buf2; + dst = pa_memblock_acquire(r->leftover_buf->memblock); + memmove(dst, buf, len); + pa_memblock_release(r->leftover_buf->memblock); } static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) { - unsigned in_n_frames, in_n_samples; - unsigned out_n_frames, out_n_samples; + unsigned in_n_frames, out_n_frames, leftover_n_frames; pa_assert(r); pa_assert(input); - /* Resample the data and place the result in buf3 */ + /* Resample the data and place the result in resample_buf. */ - if (!r->impl_resample || !input->length) + if (!r->impl.resample || !input->length) return input; - in_n_samples = (unsigned) (input->length / r->w_sz); - in_n_frames = (unsigned) (in_n_samples / r->o_ss.channels); + in_n_frames = (unsigned) (input->length / r->w_fz); out_n_frames = ((in_n_frames*r->o_ss.rate)/r->i_ss.rate)+EXTRA_FRAMES; - out_n_samples = out_n_frames * r->o_ss.channels; + fit_buf(r, &r->resample_buf, r->w_fz * out_n_frames, &r->resample_buf_size, 0); - r->buf3.index = 0; - r->buf3.length = r->w_sz * out_n_samples; + leftover_n_frames = r->impl.resample(r, input, in_n_frames, &r->resample_buf, &out_n_frames); - if (!r->buf3.memblock || r->buf3_samples < out_n_samples) { - if (r->buf3.memblock) - pa_memblock_unref(r->buf3.memblock); - - r->buf3_samples = out_n_samples; - r->buf3.memblock = pa_memblock_new(r->mempool, r->buf3.length); + if (leftover_n_frames > 0) { + void *leftover_data = (uint8_t *) pa_memblock_acquire_chunk(input) + (in_n_frames - leftover_n_frames) * r->w_fz; + save_leftover(r, leftover_data, leftover_n_frames * r->w_fz); + pa_memblock_release(input->memblock); } - r->impl_resample(r, input, in_n_frames, &r->buf3, &out_n_frames); - r->buf3.length = out_n_frames * r->w_sz * r->o_ss.channels; + r->resample_buf.length = out_n_frames * r->w_fz; - return &r->buf3; + return &r->resample_buf; } static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input) { @@ -1219,34 +1354,23 @@ static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input pa_assert(r); pa_assert(input); - /* Convert the data into the correct sample type and place the result in buf4 */ + /* Convert the data into the correct sample type and place the result in + * from_work_format_buf. */ if (!r->from_work_format_func || !input->length) return input; n_samples = (unsigned) (input->length / r->w_sz); n_frames = n_samples / r->o_ss.channels; + fit_buf(r, &r->from_work_format_buf, r->o_fz * n_frames, &r->from_work_format_buf_size, 0); - r->buf4.index = 0; - r->buf4.length = r->o_fz * n_frames; - - if (!r->buf4.memblock || r->buf4_samples < n_samples) { - if (r->buf4.memblock) - pa_memblock_unref(r->buf4.memblock); - - r->buf4_samples = n_samples; - r->buf4.memblock = pa_memblock_new(r->mempool, r->buf4.length); - } - - src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index; - dst = pa_memblock_acquire(r->buf4.memblock); + src = pa_memblock_acquire_chunk(input); + dst = pa_memblock_acquire(r->from_work_format_buf.memblock); r->from_work_format_func(n_samples, src, dst); pa_memblock_release(input->memblock); - pa_memblock_release(r->buf4.memblock); - - r->buf4.length = r->o_fz * n_frames; + pa_memblock_release(r->from_work_format_buf.memblock); - return &r->buf4; + return &r->from_work_format_buf; } void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) { @@ -1261,8 +1385,16 @@ void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) buf = (pa_memchunk*) in; buf = convert_to_work_format(r, buf); - buf = remap_channels(r, buf); - buf = resample(r, buf); + + /* Try to save resampling effort: if we have more output channels than + * input channels, do resampling first, then remapping. */ + if (r->o_ss.channels <= r->i_ss.channels) { + buf = remap_channels(r, buf); + buf = resample(r, buf); + } else { + buf = resample(r, buf); + buf = remap_channels(r, buf); + } if (buf->length) { buf = convert_from_work_format(r, buf); @@ -1279,65 +1411,76 @@ void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) /*** libsamplerate based implementation ***/ #ifdef HAVE_LIBSAMPLERATE -static void libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { +static unsigned libsamplerate_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { SRC_DATA data; + SRC_STATE *state; pa_assert(r); pa_assert(input); pa_assert(output); pa_assert(out_n_frames); + state = r->impl.data; memset(&data, 0, sizeof(data)); - data.data_in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index); + data.data_in = pa_memblock_acquire_chunk(input); data.input_frames = (long int) in_n_frames; - data.data_out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index); + data.data_out = pa_memblock_acquire_chunk(output); data.output_frames = (long int) *out_n_frames; data.src_ratio = (double) r->o_ss.rate / r->i_ss.rate; data.end_of_input = 0; - pa_assert_se(src_process(r->src.state, &data) == 0); - pa_assert((unsigned) data.input_frames_used == in_n_frames); + pa_assert_se(src_process(state, &data) == 0); pa_memblock_release(input->memblock); pa_memblock_release(output->memblock); *out_n_frames = (unsigned) data.output_frames_gen; + + return in_n_frames - data.input_frames_used; } static void libsamplerate_update_rates(pa_resampler *r) { + SRC_STATE *state; pa_assert(r); - pa_assert_se(src_set_ratio(r->src.state, (double) r->o_ss.rate / r->i_ss.rate) == 0); + state = r->impl.data; + pa_assert_se(src_set_ratio(state, (double) r->o_ss.rate / r->i_ss.rate) == 0); } static void libsamplerate_reset(pa_resampler *r) { + SRC_STATE *state; pa_assert(r); - pa_assert_se(src_reset(r->src.state) == 0); + state = r->impl.data; + pa_assert_se(src_reset(state) == 0); } static void libsamplerate_free(pa_resampler *r) { + SRC_STATE *state; pa_assert(r); - if (r->src.state) - src_delete(r->src.state); + state = r->impl.data; + if (state) + src_delete(state); } static int libsamplerate_init(pa_resampler *r) { int err; + SRC_STATE *state; pa_assert(r); - if (!(r->src.state = src_new(r->method, r->o_ss.channels, &err))) + if (!(state = src_new(r->method, r->work_channels, &err))) return -1; - r->impl_free = libsamplerate_free; - r->impl_update_rates = libsamplerate_update_rates; - r->impl_resample = libsamplerate_resample; - r->impl_reset = libsamplerate_reset; + r->impl.free = libsamplerate_free; + r->impl.update_rates = libsamplerate_update_rates; + r->impl.resample = libsamplerate_resample; + r->impl.reset = libsamplerate_reset; + r->impl.data = state; return 0; } @@ -1345,128 +1488,186 @@ static int libsamplerate_init(pa_resampler *r) { /*** speex based implementation ***/ -static void speex_resample_float(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { +static bool speex_is_fixed_point(void) { + static bool result = false; +#ifdef HAVE_SPEEX + PA_ONCE_BEGIN { + float f_out = -1.0f, f_in = 1.0f; + spx_uint32_t in_len = 1, out_len = 1; + SpeexResamplerState *s; + + pa_assert_se(s = speex_resampler_init(1, 1, 1, + SPEEX_RESAMPLER_QUALITY_MIN, NULL)); + + /* feed one sample that is too soft for fixed-point speex */ + pa_assert_se(speex_resampler_process_float(s, 0, &f_in, &in_len, + &f_out, &out_len) == RESAMPLER_ERR_SUCCESS); + + /* expecting sample has been processed, one sample output */ + pa_assert_se(in_len == 1 && out_len == 1); + + /* speex compiled with --enable-fixed-point will output 0.0 due to insufficient precision */ + if (fabsf(f_out) < 0.00001f) + result = true; + + speex_resampler_destroy(s); + } PA_ONCE_END; +#endif + return result; +} + +#ifdef HAVE_SPEEX +static unsigned speex_resample_float(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { float *in, *out; uint32_t inf = in_n_frames, outf = *out_n_frames; + SpeexResamplerState *state; pa_assert(r); pa_assert(input); pa_assert(output); pa_assert(out_n_frames); - in = (float*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index); - out = (float*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index); + state = r->impl.data; + + in = pa_memblock_acquire_chunk(input); + out = pa_memblock_acquire_chunk(output); - pa_assert_se(speex_resampler_process_interleaved_float(r->speex.state, in, &inf, out, &outf) == 0); + /* Strictly speaking, speex resampler expects its input + * to be normalized to the [-32768.0 .. 32767.0] range. + * This matters if speex has been compiled with --enable-fixed-point, + * because such speex will round the samples to the nearest + * integer. speex with --enable-fixed-point is therefore incompatible + * with PulseAudio's floating-point sample range [-1 .. 1]. speex + * without --enable-fixed-point works fine with this range. + * Care has been taken to call speex_resample_float() only + * for speex compiled without --enable-fixed-point. + */ + pa_assert_se(speex_resampler_process_interleaved_float(state, in, &inf, out, &outf) == 0); pa_memblock_release(input->memblock); pa_memblock_release(output->memblock); pa_assert(inf == in_n_frames); *out_n_frames = outf; + + return 0; } -static void speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { +static unsigned speex_resample_int(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { int16_t *in, *out; uint32_t inf = in_n_frames, outf = *out_n_frames; + SpeexResamplerState *state; pa_assert(r); pa_assert(input); pa_assert(output); pa_assert(out_n_frames); - in = (int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index); - out = (int16_t*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index); + state = r->impl.data; + + in = pa_memblock_acquire_chunk(input); + out = pa_memblock_acquire_chunk(output); - pa_assert_se(speex_resampler_process_interleaved_int(r->speex.state, in, &inf, out, &outf) == 0); + pa_assert_se(speex_resampler_process_interleaved_int(state, in, &inf, out, &outf) == 0); pa_memblock_release(input->memblock); pa_memblock_release(output->memblock); pa_assert(inf == in_n_frames); *out_n_frames = outf; + + return 0; } static void speex_update_rates(pa_resampler *r) { + SpeexResamplerState *state; pa_assert(r); - pa_assert_se(speex_resampler_set_rate(r->speex.state, r->i_ss.rate, r->o_ss.rate) == 0); + state = r->impl.data; + + pa_assert_se(speex_resampler_set_rate(state, r->i_ss.rate, r->o_ss.rate) == 0); } static void speex_reset(pa_resampler *r) { + SpeexResamplerState *state; pa_assert(r); - pa_assert_se(speex_resampler_reset_mem(r->speex.state) == 0); + state = r->impl.data; + + pa_assert_se(speex_resampler_reset_mem(state) == 0); } static void speex_free(pa_resampler *r) { + SpeexResamplerState *state; pa_assert(r); - if (!r->speex.state) + state = r->impl.data; + if (!state) return; - speex_resampler_destroy(r->speex.state); + speex_resampler_destroy(state); } static int speex_init(pa_resampler *r) { int q, err; + SpeexResamplerState *state; pa_assert(r); - r->impl_free = speex_free; - r->impl_update_rates = speex_update_rates; - r->impl_reset = speex_reset; + r->impl.free = speex_free; + r->impl.update_rates = speex_update_rates; + r->impl.reset = speex_reset; if (r->method >= PA_RESAMPLER_SPEEX_FIXED_BASE && r->method <= PA_RESAMPLER_SPEEX_FIXED_MAX) { q = r->method - PA_RESAMPLER_SPEEX_FIXED_BASE; - r->impl_resample = speex_resample_int; + r->impl.resample = speex_resample_int; } else { pa_assert(r->method >= PA_RESAMPLER_SPEEX_FLOAT_BASE && r->method <= PA_RESAMPLER_SPEEX_FLOAT_MAX); q = r->method - PA_RESAMPLER_SPEEX_FLOAT_BASE; - r->impl_resample = speex_resample_float; + r->impl.resample = speex_resample_float; } pa_log_info("Choosing speex quality setting %i.", q); - if (!(r->speex.state = speex_resampler_init(r->o_ss.channels, r->i_ss.rate, r->o_ss.rate, q, &err))) + if (!(state = speex_resampler_init(r->work_channels, r->i_ss.rate, r->o_ss.rate, q, &err))) return -1; + r->impl.data = state; + return 0; } +#endif /* Trivial implementation */ -static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { - size_t fz; - unsigned o_index; +static unsigned trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { + unsigned i_index, o_index; void *src, *dst; + struct trivial_data *trivial_data; pa_assert(r); pa_assert(input); pa_assert(output); pa_assert(out_n_frames); - fz = r->w_sz * r->o_ss.channels; + trivial_data = r->impl.data; - src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index; - dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index; + src = pa_memblock_acquire_chunk(input); + dst = pa_memblock_acquire_chunk(output); - for (o_index = 0;; o_index++, r->trivial.o_counter++) { - unsigned j; + for (o_index = 0;; o_index++, trivial_data->o_counter++) { + i_index = ((uint64_t) trivial_data->o_counter * r->i_ss.rate) / r->o_ss.rate; + i_index = i_index > trivial_data->i_counter ? i_index - trivial_data->i_counter : 0; - j = ((r->trivial.o_counter * r->i_ss.rate) / r->o_ss.rate); - j = j > r->trivial.i_counter ? j - r->trivial.i_counter : 0; - - if (j >= in_n_frames) + if (i_index >= in_n_frames) break; - pa_assert(o_index * fz < pa_memblock_get_length(output->memblock)); + pa_assert_fp(o_index * r->w_fz < pa_memblock_get_length(output->memblock)); - oil_memcpy((uint8_t*) dst + fz * o_index, - (uint8_t*) src + fz * j, (int) fz); + memcpy((uint8_t*) dst + r->w_fz * o_index, (uint8_t*) src + r->w_fz * i_index, (int) r->w_fz); } pa_memblock_release(input->memblock); @@ -1474,115 +1675,125 @@ static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned *out_n_frames = o_index; - r->trivial.i_counter += in_n_frames; + trivial_data->i_counter += in_n_frames; /* Normalize counters */ - while (r->trivial.i_counter >= r->i_ss.rate) { - pa_assert(r->trivial.o_counter >= r->o_ss.rate); + while (trivial_data->i_counter >= r->i_ss.rate) { + pa_assert(trivial_data->o_counter >= r->o_ss.rate); - r->trivial.i_counter -= r->i_ss.rate; - r->trivial.o_counter -= r->o_ss.rate; + trivial_data->i_counter -= r->i_ss.rate; + trivial_data->o_counter -= r->o_ss.rate; } + + return 0; } static void trivial_update_rates_or_reset(pa_resampler *r) { + struct trivial_data *trivial_data; pa_assert(r); - r->trivial.i_counter = 0; - r->trivial.o_counter = 0; + trivial_data = r->impl.data; + + trivial_data->i_counter = 0; + trivial_data->o_counter = 0; } static int trivial_init(pa_resampler*r) { + struct trivial_data *trivial_data; pa_assert(r); - r->trivial.o_counter = r->trivial.i_counter = 0; + trivial_data = pa_xnew0(struct trivial_data, 1); - r->impl_resample = trivial_resample; - r->impl_update_rates = trivial_update_rates_or_reset; - r->impl_reset = trivial_update_rates_or_reset; + r->impl.resample = trivial_resample; + r->impl.update_rates = trivial_update_rates_or_reset; + r->impl.reset = trivial_update_rates_or_reset; + r->impl.data = trivial_data; return 0; } /* Peak finder implementation */ -static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { - size_t fz; - unsigned o_index; +static unsigned peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { + unsigned c, o_index = 0; + unsigned i, i_end = 0; void *src, *dst; - unsigned start = 0; + struct peaks_data *peaks_data; pa_assert(r); pa_assert(input); pa_assert(output); pa_assert(out_n_frames); - fz = r->w_sz * r->o_ss.channels; + peaks_data = r->impl.data; + src = pa_memblock_acquire_chunk(input); + dst = pa_memblock_acquire_chunk(output); - src = (uint8_t*) pa_memblock_acquire(input->memblock) + input->index; - dst = (uint8_t*) pa_memblock_acquire(output->memblock) + output->index; + i = ((uint64_t) peaks_data->o_counter * r->i_ss.rate) / r->o_ss.rate; + i = i > peaks_data->i_counter ? i - peaks_data->i_counter : 0; - for (o_index = 0;; o_index++, r->peaks.o_counter++) { - unsigned j; + while (i_end < in_n_frames) { + i_end = ((uint64_t) (peaks_data->o_counter + 1) * r->i_ss.rate) / r->o_ss.rate; + i_end = i_end > peaks_data->i_counter ? i_end - peaks_data->i_counter : 0; - j = ((r->peaks.o_counter * r->i_ss.rate) / r->o_ss.rate); + pa_assert_fp(o_index * r->w_fz < pa_memblock_get_length(output->memblock)); - if (j > r->peaks.i_counter) - j -= r->peaks.i_counter; - else - j = 0; + /* 1ch float is treated separately, because that is the common case */ + if (r->work_channels == 1 && r->work_format == PA_SAMPLE_FLOAT32NE) { + float *s = (float*) src + i; + float *d = (float*) dst + o_index; - pa_assert(o_index * fz < pa_memblock_get_length(output->memblock)); + for (; i < i_end && i < in_n_frames; i++) { + float n = fabsf(*s++); - if (r->work_format == PA_SAMPLE_S16NE) { - unsigned i, c; - int16_t *s = (int16_t*) ((uint8_t*) src + fz * start); - int16_t *d = (int16_t*) ((uint8_t*) dst + fz * o_index); - - for (i = start; i <= j && i < in_n_frames; i++) + if (n > peaks_data->max_f[0]) + peaks_data->max_f[0] = n; + } - for (c = 0; c < r->o_ss.channels; c++, s++) { - int16_t n; + if (i == i_end) { + *d = peaks_data->max_f[0]; + peaks_data->max_f[0] = 0; + o_index++, peaks_data->o_counter++; + } + } else if (r->work_format == PA_SAMPLE_S16NE) { + int16_t *s = (int16_t*) src + r->work_channels * i; + int16_t *d = (int16_t*) dst + r->work_channels * o_index; - n = (int16_t) (*s < 0 ? -*s : *s); + for (; i < i_end && i < in_n_frames; i++) + for (c = 0; c < r->work_channels; c++) { + int16_t n = abs(*s++); - if (PA_UNLIKELY(n > r->peaks.max_i[c])) - r->peaks.max_i[c] = n; + if (n > peaks_data->max_i[c]) + peaks_data->max_i[c] = n; } - if (i >= in_n_frames) - break; - - for (c = 0; c < r->o_ss.channels; c++, d++) { - *d = r->peaks.max_i[c]; - r->peaks.max_i[c] = 0; + if (i == i_end) { + for (c = 0; c < r->work_channels; c++, d++) { + *d = peaks_data->max_i[c]; + peaks_data->max_i[c] = 0; + } + o_index++, peaks_data->o_counter++; } - } else { - unsigned i, c; - float *s = (float*) ((uint8_t*) src + fz * start); - float *d = (float*) ((uint8_t*) dst + fz * o_index); - - pa_assert(r->work_format == PA_SAMPLE_FLOAT32NE); + float *s = (float*) src + r->work_channels * i; + float *d = (float*) dst + r->work_channels * o_index; - for (i = start; i <= j && i < in_n_frames; i++) - for (c = 0; c < r->o_ss.channels; c++, s++) { - float n = fabsf(*s); + for (; i < i_end && i < in_n_frames; i++) + for (c = 0; c < r->work_channels; c++) { + float n = fabsf(*s++); - if (n > r->peaks.max_f[c]) - r->peaks.max_f[c] = n; + if (n > peaks_data->max_f[c]) + peaks_data->max_f[c] = n; } - if (i >= in_n_frames) - break; - - for (c = 0; c < r->o_ss.channels; c++, d++) { - *d = r->peaks.max_f[c]; - r->peaks.max_f[c] = 0; + if (i == i_end) { + for (c = 0; c < r->work_channels; c++, d++) { + *d = peaks_data->max_f[c]; + peaks_data->max_f[c] = 0; + } + o_index++, peaks_data->o_counter++; } } - - start = j; } pa_memblock_release(input->memblock); @@ -1590,110 +1801,103 @@ static void peaks_resample(pa_resampler *r, const pa_memchunk *input, unsigned i *out_n_frames = o_index; - r->peaks.i_counter += in_n_frames; + peaks_data->i_counter += in_n_frames; /* Normalize counters */ - while (r->peaks.i_counter >= r->i_ss.rate) { - pa_assert(r->peaks.o_counter >= r->o_ss.rate); + while (peaks_data->i_counter >= r->i_ss.rate) { + pa_assert(peaks_data->o_counter >= r->o_ss.rate); - r->peaks.i_counter -= r->i_ss.rate; - r->peaks.o_counter -= r->o_ss.rate; + peaks_data->i_counter -= r->i_ss.rate; + peaks_data->o_counter -= r->o_ss.rate; } + + return 0; } static void peaks_update_rates_or_reset(pa_resampler *r) { + struct peaks_data *peaks_data; pa_assert(r); - r->peaks.i_counter = 0; - r->peaks.o_counter = 0; + peaks_data = r->impl.data; + + peaks_data->i_counter = 0; + peaks_data->o_counter = 0; } static int peaks_init(pa_resampler*r) { + struct peaks_data *peaks_data; pa_assert(r); + pa_assert(r->i_ss.rate >= r->o_ss.rate); + pa_assert(r->work_format == PA_SAMPLE_S16NE || r->work_format == PA_SAMPLE_FLOAT32NE); - r->peaks.o_counter = r->peaks.i_counter = 0; - memset(r->peaks.max_i, 0, sizeof(r->peaks.max_i)); - memset(r->peaks.max_f, 0, sizeof(r->peaks.max_f)); + peaks_data = pa_xnew0(struct peaks_data, 1); - r->impl_resample = peaks_resample; - r->impl_update_rates = peaks_update_rates_or_reset; - r->impl_reset = peaks_update_rates_or_reset; + r->impl.resample = peaks_resample; + r->impl.update_rates = peaks_update_rates_or_reset; + r->impl.reset = peaks_update_rates_or_reset; + r->impl.data = peaks_data; return 0; } /*** ffmpeg based implementation ***/ -static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { +static unsigned ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) { unsigned used_frames = 0, c; + int previous_consumed_frames = -1; + struct ffmpeg_data *ffmpeg_data; pa_assert(r); pa_assert(input); pa_assert(output); pa_assert(out_n_frames); - for (c = 0; c < r->o_ss.channels; c++) { + ffmpeg_data = r->impl.data; + + for (c = 0; c < r->work_channels; c++) { unsigned u; pa_memblock *b, *w; int16_t *p, *t, *k, *q, *s; int consumed_frames; - unsigned in, l; /* Allocate a new block */ - b = pa_memblock_new(r->mempool, r->ffmpeg.buf[c].length + in_n_frames * sizeof(int16_t)); + b = pa_memblock_new(r->mempool, in_n_frames * sizeof(int16_t)); p = pa_memblock_acquire(b); - /* Copy the remaining data into it */ - l = (unsigned) r->ffmpeg.buf[c].length; - if (r->ffmpeg.buf[c].memblock) { - t = (int16_t*) ((uint8_t*) pa_memblock_acquire(r->ffmpeg.buf[c].memblock) + r->ffmpeg.buf[c].index); - memcpy(p, t, l); - pa_memblock_release(r->ffmpeg.buf[c].memblock); - pa_memblock_unref(r->ffmpeg.buf[c].memblock); - pa_memchunk_reset(&r->ffmpeg.buf[c]); - } - - /* Now append the new data, splitting up channels */ - t = ((int16_t*) ((uint8_t*) pa_memblock_acquire(input->memblock) + input->index)) + c; - k = (int16_t*) ((uint8_t*) p + l); + /* Now copy the input data, splitting up channels */ + t = (int16_t*) pa_memblock_acquire_chunk(input) + c; + k = p; for (u = 0; u < in_n_frames; u++) { *k = *t; - t += r->o_ss.channels; + t += r->work_channels; k ++; } pa_memblock_release(input->memblock); - /* Calculate the resulting number of frames */ - in = (unsigned) in_n_frames + l / (unsigned) sizeof(int16_t); - /* Allocate buffer for the result */ w = pa_memblock_new(r->mempool, *out_n_frames * sizeof(int16_t)); q = pa_memblock_acquire(w); /* Now, resample */ - used_frames = (unsigned) av_resample(r->ffmpeg.state, + used_frames = (unsigned) av_resample(ffmpeg_data->state, q, p, &consumed_frames, - (int) in, (int) *out_n_frames, - c >= (unsigned) (r->o_ss.channels-1)); + (int) in_n_frames, (int) *out_n_frames, + c >= (unsigned) (r->work_channels-1)); pa_memblock_release(b); + pa_memblock_unref(b); - /* Now store the remaining samples away */ - pa_assert(consumed_frames <= (int) in); - if (consumed_frames < (int) in) { - r->ffmpeg.buf[c].memblock = b; - r->ffmpeg.buf[c].index = (size_t) consumed_frames * sizeof(int16_t); - r->ffmpeg.buf[c].length = (size_t) (in - (unsigned) consumed_frames) * sizeof(int16_t); - } else - pa_memblock_unref(b); + pa_assert(consumed_frames <= (int) in_n_frames); + pa_assert(previous_consumed_frames == -1 || consumed_frames == previous_consumed_frames); + previous_consumed_frames = consumed_frames; /* And place the results in the output buffer */ - s = (short*) ((uint8_t*) pa_memblock_acquire(output->memblock) + output->index) + c; + s = (int16_t *) pa_memblock_acquire_chunk(output) + c; for (u = 0; u < used_frames; u++) { *s = *q; q++; - s += r->o_ss.channels; + s += r->work_channels; } pa_memblock_release(output->memblock); pa_memblock_release(w); @@ -1701,39 +1905,38 @@ static void ffmpeg_resample(pa_resampler *r, const pa_memchunk *input, unsigned } *out_n_frames = used_frames; + + return in_n_frames - previous_consumed_frames; } static void ffmpeg_free(pa_resampler *r) { - unsigned c; + struct ffmpeg_data *ffmpeg_data; pa_assert(r); - if (r->ffmpeg.state) - av_resample_close(r->ffmpeg.state); - - for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++) - if (r->ffmpeg.buf[c].memblock) - pa_memblock_unref(r->ffmpeg.buf[c].memblock); + ffmpeg_data = r->impl.data; + if (ffmpeg_data->state) + av_resample_close(ffmpeg_data->state); } static int ffmpeg_init(pa_resampler *r) { - unsigned c; + struct ffmpeg_data *ffmpeg_data; pa_assert(r); + ffmpeg_data = pa_xnew(struct ffmpeg_data, 1); + /* We could probably implement different quality levels by * adjusting the filter parameters here. However, ffmpeg * internally only uses these hardcoded values, so let's use them * here for now as well until ffmpeg makes this configurable. */ - if (!(r->ffmpeg.state = av_resample_init((int) r->o_ss.rate, (int) r->i_ss.rate, 16, 10, 0, 0.8))) + if (!(ffmpeg_data->state = av_resample_init((int) r->o_ss.rate, (int) r->i_ss.rate, 16, 10, 0, 0.8))) return -1; - r->impl_free = ffmpeg_free; - r->impl_resample = ffmpeg_resample; - - for (c = 0; c < PA_ELEMENTSOF(r->ffmpeg.buf); c++) - pa_memchunk_reset(&r->ffmpeg.buf[c]); + r->impl.free = ffmpeg_free; + r->impl.resample = ffmpeg_resample; + r->impl.data = (void *) ffmpeg_data; return 0; }