Signed-off-by: Peter Meerwald <pmeerw at pmeerw.net> Signed-off-by: poljar (Damir Jeli?) <poljarinho at gmail.com> -- changes over https://github.com/poljar/pulseaudio/commits/resampler_implementations_v2: rebased --- src/Makefile.am | 18 +- src/pulsecore/resampler.c | 634 ++------------------------------ src/pulsecore/resampler.h | 50 +++ src/pulsecore/resampler/ffmpeg.c | 132 +++++++ src/pulsecore/resampler/libsamplerate.c | 102 +++++ src/pulsecore/resampler/peaks.c | 163 ++++++++ src/pulsecore/resampler/speex.c | 151 ++++++++ src/pulsecore/resampler/trivial.c | 102 +++++ 8 files changed, 748 insertions(+), 604 deletions(-) create mode 100644 src/pulsecore/resampler/ffmpeg.c create mode 100644 src/pulsecore/resampler/libsamplerate.c create mode 100644 src/pulsecore/resampler/peaks.c create mode 100644 src/pulsecore/resampler/speex.c create mode 100644 src/pulsecore/resampler/trivial.c diff --git a/src/Makefile.am b/src/Makefile.am index 5924bd8..21eb365 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -901,6 +901,8 @@ libpulsecore_ at PA_MAJORMINOR@_la_SOURCES = \ pulsecore/remap.c pulsecore/remap.h \ pulsecore/remap_mmx.c pulsecore/remap_sse.c \ pulsecore/resampler.c pulsecore/resampler.h \ + pulsecore/resampler/ffmpeg.c pulsecore/resampler/peaks.c \ + pulsecore/resampler/trivial.c \ pulsecore/rtpoll.c pulsecore/rtpoll.h \ pulsecore/stream-util.c pulsecore/stream-util.h \ pulsecore/mix.c pulsecore/mix.h \ @@ -925,9 +927,9 @@ libpulsecore_ at PA_MAJORMINOR@_la_SOURCES = \ pulsecore/thread-mq.c pulsecore/thread-mq.h \ pulsecore/database.h -libpulsecore_ at PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) $(LIBSAMPLERATE_CFLAGS) $(LIBSPEEX_CFLAGS) $(LIBSNDFILE_CFLAGS) $(WINSOCK_CFLAGS) +libpulsecore_ at PA_MAJORMINOR@_la_CFLAGS = $(AM_CFLAGS) $(SERVER_CFLAGS) $(LIBSNDFILE_CFLAGS) $(WINSOCK_CFLAGS) libpulsecore_ at PA_MAJORMINOR@_la_LDFLAGS = $(AM_LDFLAGS) -avoid-version -libpulsecore_ at PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSAMPLERATE_LIBS) $(LIBSPEEX_LIBS) $(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon- at PA_MAJORMINOR@.la libpulse.la libpulsecore-foreign.la +libpulsecore_ at PA_MAJORMINOR@_la_LIBADD = $(AM_LIBADD) $(LIBLTDL) $(LIBSNDFILE_LIBS) $(WINSOCK_LIBS) $(LTLIBICONV) libpulsecommon- at PA_MAJORMINOR@.la libpulse.la libpulsecore-foreign.la if HAVE_NEON noinst_LTLIBRARIES += libpulsecore_sconv_neon.la libpulsecore_mix_neon.la libpulsecore_remap_neon.la @@ -978,6 +980,18 @@ if HAVE_SIMPLEDB libpulsecore_ at PA_MAJORMINOR@_la_SOURCES += pulsecore/database-simple.c endif +if HAVE_SPEEX +libpulsecore_ at PA_MAJORMINOR@_la_SOURCES += pulsecore/resampler/speex.c +libpulsecore_ at PA_MAJORMINOR@_la_CFLAGS += $(LIBSPEEX_CFLAGS) +libpulsecore_ at PA_MAJORMINOR@_la_LIBADD += $(LIBSPEEX_LIBS) +endif + +if HAVE_LIBSAMPLERATE +libpulsecore_ at PA_MAJORMINOR@_la_SOURCES += pulsecore/resampler/libsamplerate.c +libpulsecore_ at PA_MAJORMINOR@_la_CFLAGS += $(LIBSAMPLERATE_CFLAGS) +libpulsecore_ at PA_MAJORMINOR@_la_LIBADD += $(LIBSAMPLERATE_LIBS) +endif + # We split the foreign code off to not be annoyed by warnings we don't care about noinst_LTLIBRARIES += libpulsecore-foreign.la diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c index 2842e65..de58f3f 100644 --- a/src/pulsecore/resampler.c +++ b/src/pulsecore/resampler.c @@ -25,106 +25,39 @@ #include <string.h> -#ifdef HAVE_LIBSAMPLERATE -#include <samplerate.h> -#endif - #ifdef HAVE_SPEEX #include <speex/speex_resampler.h> +#include <math.h> #endif #include <pulse/xmalloc.h> -#include <pulsecore/sconv.h> #include <pulsecore/log.h> #include <pulsecore/macro.h> #include <pulsecore/strbuf.h> -#include <pulsecore/remap.h> #include <pulsecore/once.h> #include <pulsecore/core-util.h> -#include "ffmpeg/avcodec.h" #include "resampler.h" /* Number of samples of extra space we allow the resamplers to return */ #define EXTRA_FRAMES 128 -struct pa_resampler { - pa_resample_method_t method; - pa_resample_flags_t flags; - - pa_sample_spec i_ss, o_ss; - pa_channel_map i_cm, o_cm; - size_t i_fz, o_fz, w_fz, w_sz; - pa_mempool *mempool; - - 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; - - pa_remap_t remap; - bool map_required; - - pa_resampler_impl impl; -}; - -struct trivial_data { /* data specific to the trivial resampler */ - unsigned o_counter; - unsigned i_counter; -}; - -struct peaks_data { /* 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]; -}; - 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 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) = { +static int (* const init_table[])(pa_resampler *r) = { #ifdef HAVE_LIBSAMPLERATE - [PA_RESAMPLER_SRC_SINC_BEST_QUALITY] = libsamplerate_init, - [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = libsamplerate_init, - [PA_RESAMPLER_SRC_SINC_FASTEST] = libsamplerate_init, - [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD] = libsamplerate_init, - [PA_RESAMPLER_SRC_LINEAR] = libsamplerate_init, + [PA_RESAMPLER_SRC_SINC_BEST_QUALITY] = pa_resampler_libsamplerate_init, + [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = pa_resampler_libsamplerate_init, + [PA_RESAMPLER_SRC_SINC_FASTEST] = pa_resampler_libsamplerate_init, + [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD] = pa_resampler_libsamplerate_init, + [PA_RESAMPLER_SRC_LINEAR] = pa_resampler_libsamplerate_init, #else [PA_RESAMPLER_SRC_SINC_BEST_QUALITY] = NULL, [PA_RESAMPLER_SRC_SINC_MEDIUM_QUALITY] = NULL, @@ -132,30 +65,30 @@ static int (* const init_table[])(pa_resampler*r) = { [PA_RESAMPLER_SRC_ZERO_ORDER_HOLD] = NULL, [PA_RESAMPLER_SRC_LINEAR] = NULL, #endif - [PA_RESAMPLER_TRIVIAL] = trivial_init, + [PA_RESAMPLER_TRIVIAL] = pa_resampler_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, - [PA_RESAMPLER_SPEEX_FLOAT_BASE+3] = speex_init, - [PA_RESAMPLER_SPEEX_FLOAT_BASE+4] = speex_init, - [PA_RESAMPLER_SPEEX_FLOAT_BASE+5] = speex_init, - [PA_RESAMPLER_SPEEX_FLOAT_BASE+6] = speex_init, - [PA_RESAMPLER_SPEEX_FLOAT_BASE+7] = speex_init, - [PA_RESAMPLER_SPEEX_FLOAT_BASE+8] = speex_init, - [PA_RESAMPLER_SPEEX_FLOAT_BASE+9] = speex_init, - [PA_RESAMPLER_SPEEX_FLOAT_BASE+10] = speex_init, - [PA_RESAMPLER_SPEEX_FIXED_BASE+0] = speex_init, - [PA_RESAMPLER_SPEEX_FIXED_BASE+1] = speex_init, - [PA_RESAMPLER_SPEEX_FIXED_BASE+2] = speex_init, - [PA_RESAMPLER_SPEEX_FIXED_BASE+3] = speex_init, - [PA_RESAMPLER_SPEEX_FIXED_BASE+4] = speex_init, - [PA_RESAMPLER_SPEEX_FIXED_BASE+5] = speex_init, - [PA_RESAMPLER_SPEEX_FIXED_BASE+6] = speex_init, - [PA_RESAMPLER_SPEEX_FIXED_BASE+7] = speex_init, - [PA_RESAMPLER_SPEEX_FIXED_BASE+8] = speex_init, - [PA_RESAMPLER_SPEEX_FIXED_BASE+9] = speex_init, - [PA_RESAMPLER_SPEEX_FIXED_BASE+10] = speex_init, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+0] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+1] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+2] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+3] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+4] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+5] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+6] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+7] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+8] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+9] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FLOAT_BASE+10] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FIXED_BASE+0] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FIXED_BASE+1] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FIXED_BASE+2] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FIXED_BASE+3] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FIXED_BASE+4] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FIXED_BASE+5] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FIXED_BASE+6] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FIXED_BASE+7] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FIXED_BASE+8] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FIXED_BASE+9] = pa_resampler_speex_init, + [PA_RESAMPLER_SPEEX_FIXED_BASE+10] = pa_resampler_speex_init, #else [PA_RESAMPLER_SPEEX_FLOAT_BASE+0] = NULL, [PA_RESAMPLER_SPEEX_FLOAT_BASE+1] = NULL, @@ -180,10 +113,10 @@ static int (* const init_table[])(pa_resampler*r) = { [PA_RESAMPLER_SPEEX_FIXED_BASE+9] = NULL, [PA_RESAMPLER_SPEEX_FIXED_BASE+10] = NULL, #endif - [PA_RESAMPLER_FFMPEG] = ffmpeg_init, + [PA_RESAMPLER_FFMPEG] = pa_resampler_ffmpeg_init, [PA_RESAMPLER_AUTO] = NULL, [PA_RESAMPLER_COPY] = copy_init, - [PA_RESAMPLER_PEAKS] = peaks_init, + [PA_RESAMPLER_PEAKS] = pa_resampler_peaks_init, }; static bool speex_is_fixed_point(void); @@ -1408,84 +1341,6 @@ void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out) pa_memchunk_reset(out); } -/*** libsamplerate based implementation ***/ - -#ifdef HAVE_LIBSAMPLERATE -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 = pa_memblock_acquire_chunk(input); - data.input_frames = (long int) in_n_frames; - - 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(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); - - 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); - - state = r->impl.data; - pa_assert_se(src_reset(state) == 0); -} - -static void libsamplerate_free(pa_resampler *r) { - SRC_STATE *state; - pa_assert(r); - - 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 (!(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.data = state; - - return 0; -} -#endif - /*** speex based implementation ***/ static bool speex_is_fixed_point(void) { @@ -1516,431 +1371,6 @@ static bool speex_is_fixed_point(void) { 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); - - state = r->impl.data; - - in = pa_memblock_acquire_chunk(input); - out = pa_memblock_acquire_chunk(output); - - /* 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 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); - - state = r->impl.data; - - in = pa_memblock_acquire_chunk(input); - out = pa_memblock_acquire_chunk(output); - - 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); - - 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); - - 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); - - state = r->impl.data; - if (!state) - return; - - 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; - - 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; - - } 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; - } - - pa_log_info("Choosing speex quality setting %i.", q); - - 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 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); - - trivial_data = r->impl.data; - - src = pa_memblock_acquire_chunk(input); - dst = pa_memblock_acquire_chunk(output); - - 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; - - if (i_index >= in_n_frames) - break; - - pa_assert_fp(o_index * r->w_fz < pa_memblock_get_length(output->memblock)); - - 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); - pa_memblock_release(output->memblock); - - *out_n_frames = o_index; - - trivial_data->i_counter += in_n_frames; - - /* Normalize counters */ - while (trivial_data->i_counter >= r->i_ss.rate) { - pa_assert(trivial_data->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); - - 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); - - 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.data = trivial_data; - - return 0; -} - -/* Peak finder implementation */ - -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; - struct peaks_data *peaks_data; - - pa_assert(r); - pa_assert(input); - pa_assert(output); - pa_assert(out_n_frames); - - peaks_data = r->impl.data; - src = pa_memblock_acquire_chunk(input); - dst = pa_memblock_acquire_chunk(output); - - 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; - - 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; - - pa_assert_fp(o_index * r->w_fz < pa_memblock_get_length(output->memblock)); - - /* 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; - - for (; i < i_end && i < in_n_frames; i++) { - float n = fabsf(*s++); - - if (n > peaks_data->max_f[0]) - peaks_data->max_f[0] = 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; - - for (; i < i_end && i < in_n_frames; i++) - for (c = 0; c < r->work_channels; c++) { - int16_t n = abs(*s++); - - if (n > peaks_data->max_i[c]) - peaks_data->max_i[c] = n; - } - - 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 { - float *s = (float*) src + r->work_channels * i; - float *d = (float*) dst + r->work_channels * o_index; - - for (; i < i_end && i < in_n_frames; i++) - for (c = 0; c < r->work_channels; c++) { - float n = fabsf(*s++); - - if (n > peaks_data->max_f[c]) - peaks_data->max_f[c] = n; - } - - 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++; - } - } - } - - pa_memblock_release(input->memblock); - pa_memblock_release(output->memblock); - - *out_n_frames = o_index; - - peaks_data->i_counter += in_n_frames; - - /* Normalize counters */ - while (peaks_data->i_counter >= r->i_ss.rate) { - pa_assert(peaks_data->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); - - 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); - - 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.data = peaks_data; - - return 0; -} - -/*** ffmpeg based implementation ***/ - -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); - - 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; - - /* Allocate a new block */ - b = pa_memblock_new(r->mempool, in_n_frames * sizeof(int16_t)); - p = pa_memblock_acquire(b); - - /* 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->work_channels; - k ++; - } - pa_memblock_release(input->memblock); - - /* 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(ffmpeg_data->state, - q, p, - &consumed_frames, - (int) in_n_frames, (int) *out_n_frames, - c >= (unsigned) (r->work_channels-1)); - - pa_memblock_release(b); - 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 = (int16_t *) pa_memblock_acquire_chunk(output) + c; - for (u = 0; u < used_frames; u++) { - *s = *q; - q++; - s += r->work_channels; - } - pa_memblock_release(output->memblock); - pa_memblock_release(w); - pa_memblock_unref(w); - } - - *out_n_frames = used_frames; - - return in_n_frames - previous_consumed_frames; -} - -static void ffmpeg_free(pa_resampler *r) { - struct ffmpeg_data *ffmpeg_data; - - pa_assert(r); - - ffmpeg_data = r->impl.data; - if (ffmpeg_data->state) - av_resample_close(ffmpeg_data->state); -} - -static int ffmpeg_init(pa_resampler *r) { - 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 (!(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; - r->impl.data = (void *) ffmpeg_data; - - return 0; -} - /*** copy (noop) implementation ***/ static int copy_init(pa_resampler *r) { diff --git a/src/pulsecore/resampler.h b/src/pulsecore/resampler.h index 058a800..13d8c35 100644 --- a/src/pulsecore/resampler.h +++ b/src/pulsecore/resampler.h @@ -26,10 +26,14 @@ #include <pulse/channelmap.h> #include <pulsecore/memblock.h> #include <pulsecore/memchunk.h> +#include <pulsecore/sconv.h> +#include <pulsecore/remap.h> typedef struct pa_resampler pa_resampler; typedef struct pa_resampler_impl pa_resampler_impl; +typedef struct pa_resampler pa_resampler; + struct pa_resampler_impl { void (*free)(pa_resampler *r); void (*update_rates)(pa_resampler *r); @@ -67,6 +71,45 @@ typedef enum pa_resample_flags { PA_RESAMPLER_NO_LFE = 0x0008U } pa_resample_flags_t; +struct pa_resampler { + pa_resample_method_t method; + pa_resample_flags_t flags; + + pa_sample_spec i_ss, o_ss; + pa_channel_map i_cm, o_cm; + size_t i_fz, o_fz, w_fz, w_sz; + pa_mempool *mempool; + + 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; + + pa_remap_t remap; + bool map_required; + + pa_resampler_impl impl; +}; + pa_resampler* pa_resampler_new( pa_mempool *pool, const pa_sample_spec *a, @@ -116,4 +159,11 @@ const pa_sample_spec* pa_resampler_input_sample_spec(pa_resampler *r); const pa_channel_map* pa_resampler_output_channel_map(pa_resampler *r); const pa_sample_spec* pa_resampler_output_sample_spec(pa_resampler *r); +/* Implementation specific init functions */ +int pa_resampler_ffmpeg_init(pa_resampler *r); +int pa_resampler_libsamplerate_init(pa_resampler *r); +int pa_resampler_peaks_init(pa_resampler *r); +int pa_resampler_speex_init(pa_resampler *r); +int pa_resampler_trivial_init(pa_resampler*r); + #endif diff --git a/src/pulsecore/resampler/ffmpeg.c b/src/pulsecore/resampler/ffmpeg.c new file mode 100644 index 0000000..9e35236 --- /dev/null +++ b/src/pulsecore/resampler/ffmpeg.c @@ -0,0 +1,132 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + 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.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulse/xmalloc.h> +#include "pulsecore/ffmpeg/avcodec.h" + +#include "pulsecore/resampler.h" + +struct ffmpeg_data { /* data specific to ffmpeg */ + struct AVResampleContext *state; +}; + +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); + + 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; + + /* Allocate a new block */ + b = pa_memblock_new(r->mempool, in_n_frames * sizeof(int16_t)); + p = pa_memblock_acquire(b); + + /* 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->work_channels; + k ++; + } + pa_memblock_release(input->memblock); + + /* 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(ffmpeg_data->state, + q, p, + &consumed_frames, + (int) in_n_frames, (int) *out_n_frames, + c >= (unsigned) (r->work_channels-1)); + + pa_memblock_release(b); + 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 = (int16_t *) pa_memblock_acquire_chunk(output) + c; + for (u = 0; u < used_frames; u++) { + *s = *q; + q++; + s += r->work_channels; + } + pa_memblock_release(output->memblock); + pa_memblock_release(w); + pa_memblock_unref(w); + } + + *out_n_frames = used_frames; + + return in_n_frames - previous_consumed_frames; +} + +static void ffmpeg_free(pa_resampler *r) { + struct ffmpeg_data *ffmpeg_data; + + pa_assert(r); + + ffmpeg_data = r->impl.data; + if (ffmpeg_data->state) + av_resample_close(ffmpeg_data->state); +} + +int pa_resampler_ffmpeg_init(pa_resampler *r) { + 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 (!(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; + r->impl.data = (void *) ffmpeg_data; + + return 0; +} diff --git a/src/pulsecore/resampler/libsamplerate.c b/src/pulsecore/resampler/libsamplerate.c new file mode 100644 index 0000000..48ea594 --- /dev/null +++ b/src/pulsecore/resampler/libsamplerate.c @@ -0,0 +1,102 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + 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.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <samplerate.h> + +#include "pulsecore/resampler.h" + +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 = pa_memblock_acquire_chunk(input); + data.input_frames = (long int) in_n_frames; + + 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(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); + + 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); + + state = r->impl.data; + pa_assert_se(src_reset(state) == 0); +} + +static void libsamplerate_free(pa_resampler *r) { + SRC_STATE *state; + pa_assert(r); + + state = r->impl.data; + if (state) + src_delete(state); +} + +int pa_resampler_libsamplerate_init(pa_resampler *r) { + int err; + SRC_STATE *state; + + pa_assert(r); + + 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.data = state; + + return 0; +} diff --git a/src/pulsecore/resampler/peaks.c b/src/pulsecore/resampler/peaks.c new file mode 100644 index 0000000..ef9e99e --- /dev/null +++ b/src/pulsecore/resampler/peaks.c @@ -0,0 +1,163 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + 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.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulse/xmalloc.h> +#include <math.h> + +#include "pulsecore/resampler.h" + +struct peaks_data { /* 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]; +}; + +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; + struct peaks_data *peaks_data; + + pa_assert(r); + pa_assert(input); + pa_assert(output); + pa_assert(out_n_frames); + + peaks_data = r->impl.data; + src = pa_memblock_acquire_chunk(input); + dst = pa_memblock_acquire_chunk(output); + + 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; + + 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; + + pa_assert_fp(o_index * r->w_fz < pa_memblock_get_length(output->memblock)); + + /* 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; + + for (; i < i_end && i < in_n_frames; i++) { + float n = fabsf(*s++); + + if (n > peaks_data->max_f[0]) + peaks_data->max_f[0] = 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; + + for (; i < i_end && i < in_n_frames; i++) + for (c = 0; c < r->work_channels; c++) { + int16_t n = abs(*s++); + + if (n > peaks_data->max_i[c]) + peaks_data->max_i[c] = n; + } + + 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 { + float *s = (float*) src + r->work_channels * i; + float *d = (float*) dst + r->work_channels * o_index; + + for (; i < i_end && i < in_n_frames; i++) + for (c = 0; c < r->work_channels; c++) { + float n = fabsf(*s++); + + if (n > peaks_data->max_f[c]) + peaks_data->max_f[c] = n; + } + + 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++; + } + } + } + + pa_memblock_release(input->memblock); + pa_memblock_release(output->memblock); + + *out_n_frames = o_index; + + peaks_data->i_counter += in_n_frames; + + /* Normalize counters */ + while (peaks_data->i_counter >= r->i_ss.rate) { + pa_assert(peaks_data->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); + + peaks_data = r->impl.data; + + peaks_data->i_counter = 0; + peaks_data->o_counter = 0; +} + +int pa_resampler_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); + + 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.data = peaks_data; + + return 0; +} diff --git a/src/pulsecore/resampler/speex.c b/src/pulsecore/resampler/speex.c new file mode 100644 index 0000000..faeef76 --- /dev/null +++ b/src/pulsecore/resampler/speex.c @@ -0,0 +1,151 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + 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.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <speex/speex_resampler.h> + +#include "pulsecore/resampler.h" + +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); + + state = r->impl.data; + + in = pa_memblock_acquire_chunk(input); + out = pa_memblock_acquire_chunk(output); + + /* 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 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); + + state = r->impl.data; + + in = pa_memblock_acquire_chunk(input); + out = pa_memblock_acquire_chunk(output); + + 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); + + 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); + + 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); + + state = r->impl.data; + if (!state) + return; + + speex_resampler_destroy(state); +} + +int pa_resampler_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; + + 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; + + } 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; + } + + pa_log_info("Choosing speex quality setting %i.", q); + + 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; +} diff --git a/src/pulsecore/resampler/trivial.c b/src/pulsecore/resampler/trivial.c new file mode 100644 index 0000000..09c81ee --- /dev/null +++ b/src/pulsecore/resampler/trivial.c @@ -0,0 +1,102 @@ +/*** + This file is part of PulseAudio. + + Copyright 2004-2006 Lennart Poettering + + 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.1 of the License, + or (at your option) any later version. + + PulseAudio is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with PulseAudio; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <pulse/xmalloc.h> + +#include "pulsecore/resampler.h" + +struct trivial_data { /* data specific to the trivial resampler */ + unsigned o_counter; + unsigned i_counter; +}; + +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); + + trivial_data = r->impl.data; + + src = pa_memblock_acquire_chunk(input); + dst = pa_memblock_acquire_chunk(output); + + 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; + + if (i_index >= in_n_frames) + break; + + pa_assert_fp(o_index * r->w_fz < pa_memblock_get_length(output->memblock)); + + 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); + pa_memblock_release(output->memblock); + + *out_n_frames = o_index; + + trivial_data->i_counter += in_n_frames; + + /* Normalize counters */ + while (trivial_data->i_counter >= r->i_ss.rate) { + pa_assert(trivial_data->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); + + trivial_data = r->impl.data; + + trivial_data->i_counter = 0; + trivial_data->o_counter = 0; +} + +int pa_resampler_trivial_init(pa_resampler *r) { + struct trivial_data *trivial_data; + pa_assert(r); + + 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.data = trivial_data; + + return 0; +} -- 1.9.1