diff options
Diffstat (limited to 'src/pulsecore/resampler')
-rw-r--r-- | src/pulsecore/resampler/ffmpeg.c | 132 | ||||
-rw-r--r-- | src/pulsecore/resampler/libsamplerate.c | 100 | ||||
-rw-r--r-- | src/pulsecore/resampler/peaks.c | 161 | ||||
-rw-r--r-- | src/pulsecore/resampler/soxr.c | 168 | ||||
-rw-r--r-- | src/pulsecore/resampler/speex.c | 178 | ||||
-rw-r--r-- | src/pulsecore/resampler/trivial.c | 100 |
6 files changed, 839 insertions, 0 deletions
diff --git a/src/pulsecore/resampler/ffmpeg.c b/src/pulsecore/resampler/ffmpeg.c new file mode 100644 index 0000000..388b555 --- /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, see <http://www.gnu.org/licenses/>. +***/ + +#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))) { + pa_xfree(ffmpeg_data); + 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..06704fe --- /dev/null +++ b/src/pulsecore/resampler/libsamplerate.c @@ -0,0 +1,100 @@ +/*** + 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, see <http://www.gnu.org/licenses/>. +***/ + +#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..c9b808e --- /dev/null +++ b/src/pulsecore/resampler/peaks.c @@ -0,0 +1,161 @@ +/*** + 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, see <http://www.gnu.org/licenses/>. +***/ + +#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/soxr.c b/src/pulsecore/resampler/soxr.c new file mode 100644 index 0000000..b1b2e19 --- /dev/null +++ b/src/pulsecore/resampler/soxr.c @@ -0,0 +1,168 @@ +/*** + This file is part of PulseAudio. + + Copyright 2014, 2015 Andrey Semashev + + 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, see <http://www.gnu.org/licenses/>. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stddef.h> +#include <soxr.h> + +#include <pulsecore/resampler.h> + +static unsigned resampler_soxr_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, + pa_memchunk *output, unsigned *out_n_frames) { + soxr_t state; + void *in, *out; + size_t consumed = 0, produced = 0; + + pa_assert(r); + pa_assert(input); + pa_assert(output); + pa_assert(out_n_frames); + + state = r->impl.data; + pa_assert(state); + + in = pa_memblock_acquire_chunk(input); + out = pa_memblock_acquire_chunk(output); + + pa_assert_se(soxr_process(state, in, in_n_frames, &consumed, out, *out_n_frames, &produced) == 0); + + pa_memblock_release(input->memblock); + pa_memblock_release(output->memblock); + + *out_n_frames = produced; + + return in_n_frames - consumed; +} + +static void resampler_soxr_free(pa_resampler *r) { + pa_assert(r); + + if (!r->impl.data) + return; + + soxr_delete(r->impl.data); + r->impl.data = NULL; +} + +static void resampler_soxr_reset(pa_resampler *r) { +#if SOXR_THIS_VERSION >= SOXR_VERSION(0, 1, 2) + pa_assert(r); + + soxr_clear(r->impl.data); +#else + /* With libsoxr prior to 0.1.2 soxr_clear() makes soxr_process() crash afterwards, + * so don't use this function and re-create the context instead. */ + soxr_t old_state; + + pa_assert(r); + + old_state = r->impl.data; + r->impl.data = NULL; + + if (pa_resampler_soxr_init(r) == 0) { + if (old_state) + soxr_delete(old_state); + } else { + r->impl.data = old_state; + pa_log_error("Failed to reset libsoxr context"); + } +#endif +} + +static void resampler_soxr_update_rates(pa_resampler *r) { + soxr_t old_state; + + pa_assert(r); + + /* There is no update method in libsoxr, + * so just re-create the resampler context */ + + old_state = r->impl.data; + r->impl.data = NULL; + + if (pa_resampler_soxr_init(r) == 0) { + if (old_state) + soxr_delete(old_state); + } else { + r->impl.data = old_state; + pa_log_error("Failed to update libsoxr sample rates"); + } +} + +int pa_resampler_soxr_init(pa_resampler *r) { + soxr_t state; + soxr_datatype_t io_format; + soxr_io_spec_t io_spec; + soxr_runtime_spec_t runtime_spec; + unsigned long quality_recipe; + soxr_quality_spec_t quality; + soxr_error_t err = NULL; + + pa_assert(r); + + switch (r->work_format) { + case PA_SAMPLE_S16NE: + io_format = SOXR_INT16_I; + break; + case PA_SAMPLE_FLOAT32NE: + io_format = SOXR_FLOAT32_I; + break; + default: + pa_assert_not_reached(); + } + + io_spec = soxr_io_spec(io_format, io_format); + + /* Resample in one thread. Multithreading makes + * performance worse with small chunks of audio. */ + runtime_spec = soxr_runtime_spec(1); + + switch (r->method) { + case PA_RESAMPLER_SOXR_MQ: + quality_recipe = SOXR_MQ | SOXR_LINEAR_PHASE; + break; + case PA_RESAMPLER_SOXR_HQ: + quality_recipe = SOXR_HQ | SOXR_LINEAR_PHASE; + break; + case PA_RESAMPLER_SOXR_VHQ: + quality_recipe = SOXR_VHQ | SOXR_LINEAR_PHASE; + break; + default: + pa_assert_not_reached(); + } + + quality = soxr_quality_spec(quality_recipe, 0); + + state = soxr_create(r->i_ss.rate, r->o_ss.rate, r->work_channels, &err, &io_spec, &quality, &runtime_spec); + if (!state) { + pa_log_error("Failed to create libsoxr resampler context: %s.", (err ? err : "[unknown error]")); + return -1; + } + + r->impl.free = resampler_soxr_free; + r->impl.reset = resampler_soxr_reset; + r->impl.update_rates = resampler_soxr_update_rates; + r->impl.resample = resampler_soxr_resample; + r->impl.data = state; + + return 0; +} diff --git a/src/pulsecore/resampler/speex.c b/src/pulsecore/resampler/speex.c new file mode 100644 index 0000000..66387e5 --- /dev/null +++ b/src/pulsecore/resampler/speex.c @@ -0,0 +1,178 @@ +/*** + 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, see <http://www.gnu.org/licenses/>. +***/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <speex/speex_resampler.h> +#include <math.h> + +#include <pulsecore/once.h> +#include <pulsecore/resampler.h> + +bool pa_speex_is_fixed_point(void) { + static bool result = false; + 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; + return result; +} + + +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..14e7ef3 --- /dev/null +++ b/src/pulsecore/resampler/trivial.c @@ -0,0 +1,100 @@ +/*** + 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, see <http://www.gnu.org/licenses/>. +***/ + +#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; +} |