pa_memchunk remap_buf;
pa_memchunk resample_buf;
pa_memchunk from_work_format_buf;
- unsigned to_work_format_buf_samples;
+ size_t to_work_format_buf_size;
size_t remap_buf_size;
- unsigned resample_buf_samples;
- unsigned from_work_format_buf_samples;
- bool remap_buf_contains_leftover_data;
+ 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;
struct ffmpeg_data { /* data specific to ffmpeg */
struct AVResampleContext *state;
- pa_memchunk buf[PA_CHANNELS_MAX];
};
static int copy_init(pa_resampler *r);
const uint32_t rate_a,
const uint32_t rate_b) {
- pa_assert(rate_a > 0 && rate_a <= PA_RATE_MAX);
- pa_assert(rate_b > 0 && rate_b <= PA_RATE_MAX);
+ 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);
/* 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(a >= 0 && a < PA_SAMPLE_MAX);
- pa_assert(b >= 0 && b < PA_SAMPLE_MAX);
+ pa_assert(pa_sample_format_valid(a));
+ pa_assert(pa_sample_format_valid(b));
switch (a) {
case PA_SAMPLE_U8:
bool map_required) {
pa_sample_format_t work_format;
- pa_assert(a >= 0 && a < PA_SAMPLE_MAX);
- pa_assert(b >= 0 && b < PA_SAMPLE_MAX);
+ pa_assert(pa_sample_format_valid(a));
+ pa_assert(pa_sample_format_valid(b));
pa_assert(method >= 0);
pa_assert(method < PA_RESAMPLER_MAX);
pa_log_info("Using %s as working format.", pa_sample_format_to_string(r->work_format));
r->w_sz = pa_sample_size_of_format(r->work_format);
- r->w_fz = pa_sample_size_of_format(r->work_format) * r->o_ss.channels;
if (r->i_ss.format != r->work_format) {
if (r->work_format == PA_SAMPLE_FLOAT32NE) {
}
}
+ /* leftover buffer is the buffer before the resampling stage */
+ r->leftover_buf = &r->remap_buf;
+ r->leftover_buf_size = &r->remap_buf_size;
+ r->have_leftover = &r->leftover_in_remap;
+
+ r->work_channels = r->o_ss.channels;
+ r->w_fz = pa_sample_size_of_format(r->work_format) * r->work_channels;
+
/* initialize implementation */
if (init_table[method](r) < 0)
goto fail;
* enough output buffer. */
frames = (in_length + r->i_fz - 1) / r->i_fz;
-
- if (r->remap_buf_contains_leftover_data)
- frames += r->remap_buf.length / r->w_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;
}
max_fs = pa_frame_size(&max_ss);
frames = block_size_max / max_fs - EXTRA_FRAMES;
- if (r->remap_buf_contains_leftover_data)
- frames -= r->remap_buf.length / r->w_fz;
+ 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 (r->impl.reset)
r->impl.reset(r);
- r->remap_buf_contains_leftover_data = false;
+ *r->have_leftover = false;
}
pa_resample_method_t pa_resampler_get_method(pa_resampler *r) {
pa_init_remap(m);
}
+/* 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);
+ }
+
+ pa_memblock_unref(buf->memblock);
+ }
+
+ buf->memblock = new_block;
+ *size = len;
+ }
+
+ buf->length = len;
+}
+
static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
- unsigned n_samples;
+ unsigned in_n_samples, out_n_samples;
void *src, *dst;
+ bool have_leftover;
+ size_t leftover_length = 0;
pa_assert(r);
pa_assert(input);
pa_assert(input->memblock);
/* Convert the incoming sample into the work sample format and place them
- * in to_work_format_buf. */
-
- if (!r->to_work_format_func || !input->length)
- return input;
+ * in to_work_format_buf. The leftover data is already converted, so it's
+ * part of the output buffer. */
- n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);
+ have_leftover = r->leftover_in_to_work;
+ r->leftover_in_to_work = false;
- r->to_work_format_buf.index = 0;
- r->to_work_format_buf.length = r->w_sz * n_samples;
+ if (!have_leftover && (!r->to_work_format_func || !input->length))
+ return input;
+ else if (input->length <= 0)
+ return &r->to_work_format_buf;
- if (!r->to_work_format_buf.memblock || r->to_work_format_buf_samples < n_samples) {
- if (r->to_work_format_buf.memblock)
- pa_memblock_unref(r->to_work_format_buf.memblock);
+ in_n_samples = out_n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);
- r->to_work_format_buf_samples = n_samples;
- r->to_work_format_buf.memblock = pa_memblock_new(r->mempool, r->to_work_format_buf.length);
+ if (have_leftover) {
+ leftover_length = r->to_work_format_buf.length;
+ out_n_samples += (unsigned) (leftover_length / r->w_sz);
}
+ fit_buf(r, &r->to_work_format_buf, r->w_sz * out_n_samples, &r->to_work_format_buf_size, leftover_length);
+
src = pa_memblock_acquire_chunk(input);
- dst = pa_memblock_acquire(r->to_work_format_buf.memblock);
+ dst = (uint8_t *) pa_memblock_acquire(r->to_work_format_buf.memblock) + leftover_length;
- r->to_work_format_func(n_samples, src, dst);
+ if (r->to_work_format_func)
+ r->to_work_format_func(in_n_samples, src, dst);
+ else
+ memcpy(dst, src, input->length);
pa_memblock_release(input->memblock);
pa_memblock_release(r->to_work_format_buf.memblock);
* 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->remap_buf_contains_leftover_data;
- r->remap_buf_contains_leftover_data = false;
+ have_leftover = r->leftover_in_remap;
+ r->leftover_in_remap = false;
if (!have_leftover && (!r->map_required || input->length <= 0))
return input;
}
out_n_samples = out_n_frames * r->o_ss.channels;
- r->remap_buf.length = out_n_samples * r->w_sz;
-
- if (have_leftover) {
- if (r->remap_buf_size < r->remap_buf.length) {
- pa_memblock *new_block = pa_memblock_new(r->mempool, r->remap_buf.length);
-
- src = pa_memblock_acquire(r->remap_buf.memblock);
- dst = pa_memblock_acquire(new_block);
- memcpy(dst, src, leftover_length);
- pa_memblock_release(r->remap_buf.memblock);
- pa_memblock_release(new_block);
-
- pa_memblock_unref(r->remap_buf.memblock);
- r->remap_buf.memblock = new_block;
- r->remap_buf_size = r->remap_buf.length;
- }
-
- } else {
- if (!r->remap_buf.memblock || r->remap_buf_size < r->remap_buf.length) {
- if (r->remap_buf.memblock)
- pa_memblock_unref(r->remap_buf.memblock);
-
- r->remap_buf_size = r->remap_buf.length;
- r->remap_buf.memblock = pa_memblock_new(r->mempool, r->remap_buf.length);
- }
- }
+ fit_buf(r, &r->remap_buf, out_n_samples * r->w_sz, &r->remap_buf_size, leftover_length);
src = pa_memblock_acquire_chunk(input);
dst = (uint8_t *) pa_memblock_acquire(r->remap_buf.memblock) + leftover_length;
return &r->remap_buf;
}
+static void save_leftover(pa_resampler *r, void *buf, size_t len) {
+ void *dst;
+
+ pa_assert(r);
+ pa_assert(buf);
+ pa_assert(len > 0);
+
+ /* Store the leftover data. */
+ fit_buf(r, r->leftover_buf, len, r->leftover_buf_size, 0);
+ *r->have_leftover = true;
+
+ 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);
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;
-
- r->resample_buf.index = 0;
- r->resample_buf.length = r->w_sz * out_n_samples;
+ fit_buf(r, &r->resample_buf, r->w_fz * out_n_frames, &r->resample_buf_size, 0);
- if (!r->resample_buf.memblock || r->resample_buf_samples < out_n_samples) {
- if (r->resample_buf.memblock)
- pa_memblock_unref(r->resample_buf.memblock);
+ leftover_n_frames = r->impl.resample(r, input, in_n_frames, &r->resample_buf, &out_n_frames);
- r->resample_buf_samples = out_n_samples;
- r->resample_buf.memblock = pa_memblock_new(r->mempool, r->resample_buf.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->resample_buf, &out_n_frames);
r->resample_buf.length = out_n_frames * r->w_fz;
return &r->resample_buf;
n_samples = (unsigned) (input->length / r->w_sz);
n_frames = n_samples / r->o_ss.channels;
-
- r->from_work_format_buf.index = 0;
- r->from_work_format_buf.length = r->o_fz * n_frames;
-
- if (!r->from_work_format_buf.memblock || r->from_work_format_buf_samples < n_samples) {
- if (r->from_work_format_buf.memblock)
- pa_memblock_unref(r->from_work_format_buf.memblock);
-
- r->from_work_format_buf_samples = n_samples;
- r->from_work_format_buf.memblock = pa_memblock_new(r->mempool, r->from_work_format_buf.length);
- }
+ fit_buf(r, &r->from_work_format_buf, r->o_fz * n_frames, &r->from_work_format_buf_size, 0);
src = pa_memblock_acquire_chunk(input);
dst = pa_memblock_acquire(r->from_work_format_buf.memblock);
pa_memchunk_reset(out);
}
-static void save_leftover(pa_resampler *r, void *buf, size_t len) {
- void *dst;
-
- pa_assert(r);
- pa_assert(buf);
- pa_assert(len > 0);
-
- /* Store the leftover to remap_buf. */
-
- r->remap_buf.length = len;
-
- if (!r->remap_buf.memblock || r->remap_buf_size < r->remap_buf.length) {
- if (r->remap_buf.memblock)
- pa_memblock_unref(r->remap_buf.memblock);
-
- r->remap_buf_size = r->remap_buf.length;
- r->remap_buf.memblock = pa_memblock_new(r->mempool, r->remap_buf.length);
- }
-
- dst = pa_memblock_acquire(r->remap_buf.memblock);
- memcpy(dst, buf, r->remap_buf.length);
- pa_memblock_release(r->remap_buf.memblock);
-
- r->remap_buf_contains_leftover_data = true;
-}
-
/*** 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_se(src_process(state, &data) == 0);
- if (data.input_frames_used < in_n_frames) {
- void *leftover_data = data.data_in + data.input_frames_used * r->o_ss.channels;
- size_t leftover_length = (in_n_frames - data.input_frames_used) * r->w_fz;
-
- save_leftover(r, leftover_data, leftover_length);
- }
-
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) {
pa_assert(r);
- if (!(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;
#ifdef HAVE_SPEEX
/*** 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 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(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(inf == in_n_frames);
*out_n_frames = outf;
+
+ return 0;
}
static void speex_update_rates(pa_resampler *r) {
pa_log_info("Choosing speex quality setting %i.", q);
- if (!(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;
/* Trivial implementation */
-static void trivial_resample(pa_resampler *r, const pa_memchunk *input, unsigned in_n_frames, pa_memchunk *output, unsigned *out_n_frames) {
+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;
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) {
/* 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) {
+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;
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->o_ss.channels == 1 && r->work_format == PA_SAMPLE_FLOAT32NE) {
+ if (r->work_channels == 1 && r->work_format == PA_SAMPLE_FLOAT32NE) {
float *s = (float*) src + i;
float *d = (float*) dst + o_index;
o_index++, peaks_data->o_counter++;
}
} else if (r->work_format == PA_SAMPLE_S16NE) {
- int16_t *s = (int16_t*) src + r->o_ss.channels * i;
- int16_t *d = (int16_t*) dst + r->o_ss.channels * o_index;
+ 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->o_ss.channels; c++) {
+ for (c = 0; c < r->work_channels; c++) {
int16_t n = abs(*s++);
if (n > peaks_data->max_i[c])
}
if (i == i_end) {
- for (c = 0; c < r->o_ss.channels; c++, d++) {
+ 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->o_ss.channels * i;
- float *d = (float*) dst + r->o_ss.channels * o_index;
+ 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->o_ss.channels; c++) {
+ for (c = 0; c < r->work_channels; c++) {
float n = fabsf(*s++);
if (n > peaks_data->max_f[c])
}
if (i == i_end) {
- for (c = 0; c < r->o_ss.channels; c++, d++) {
+ for (c = 0; c < r->work_channels; c++, d++) {
*d = peaks_data->max_f[c];
peaks_data->max_f[c] = 0;
}
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) {
/*** 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;
ffmpeg_data = r->impl.data;
- for (c = 0; c < r->o_ss.channels; c++) {
+ 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, ffmpeg_data->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);
/* Now copy the input data, splitting up channels */
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);
q, p,
&consumed_frames,
(int) in_n_frames, (int) *out_n_frames,
- c >= (unsigned) (r->o_ss.channels-1));
+ c >= (unsigned) (r->work_channels-1));
pa_memblock_release(b);
pa_memblock_unref(b);
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);
pa_memblock_unref(w);
}
- if (previous_consumed_frames < (int) in_n_frames) {
- void *leftover_data = (int16_t *) pa_memblock_acquire_chunk(input) + previous_consumed_frames * r->o_ss.channels;
- size_t leftover_length = (in_n_frames - previous_consumed_frames) * r->w_fz;
-
- save_leftover(r, leftover_data, leftover_length);
- pa_memblock_release(input->memblock);
- }
-
*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);
ffmpeg_data = r->impl.data;
if (ffmpeg_data->state)
av_resample_close(ffmpeg_data->state);
-
- for (c = 0; c < PA_ELEMENTSOF(ffmpeg_data->buf); c++)
- if (ffmpeg_data->buf[c].memblock)
- pa_memblock_unref(ffmpeg_data->buf[c].memblock);
}
static int ffmpeg_init(pa_resampler *r) {
- unsigned c;
struct ffmpeg_data *ffmpeg_data;
pa_assert(r);
r->impl.resample = ffmpeg_resample;
r->impl.data = (void *) ffmpeg_data;
- for (c = 0; c < PA_ELEMENTSOF(ffmpeg_data->buf); c++)
- pa_memchunk_reset(&ffmpeg_data->buf[c]);
-
return 0;
}