size_t resample_buf_size;
size_t from_work_format_buf_size;
- /* points to buffer before resampling stage, remap */
+ /* 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 */
+ /* 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;
static int libsamplerate_init(pa_resampler*r);
#endif
-static void calc_map_table(pa_resampler *r);
+static void calc_map_table(const pa_resampler *r, pa_remap_t *m);
static int (* const init_table[])(pa_resampler*r) = {
#ifdef HAVE_LIBSAMPLERATE
r->i_fz = pa_frame_size(a);
r->o_fz = pa_frame_size(b);
- calc_map_table(r);
-
- pa_log_info("Using resampler '%s'", pa_resample_method_to_string(method));
+ /* compute channel remap table if needed */
+ 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)))))
+ calc_map_table(r, &r->remap);
r->work_format = pa_resampler_choose_work_format(method, a->format, b->format, r->map_required);
-
- 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);
if (r->i_ss.format != r->work_format) {
}
}
- /* 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;
+ if (r->o_ss.channels <= r->i_ss.channels) {
+ /* pipeline is: format conv. -> remap -> resample -> format conv. */
+ r->work_channels = r->o_ss.channels;
- r->work_channels = r->o_ss.channels;
+ /* 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 {
+ /* pipeline is: format conv. -> resample -> remap -> format conv. */
+ r->work_channels = r->i_ss.channels;
+
+ /* 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);
+
/* initialize implementation */
if (init_table[method](r) < 0)
goto fail;
return ON_OTHER;
}
-static void calc_map_table(pa_resampler *r) {
+static void calc_map_table(const pa_resampler *r, pa_remap_t *m) {
unsigned oc, ic;
unsigned n_oc, n_ic;
bool ic_connected[PA_CHANNELS_MAX];
bool remix;
pa_strbuf *s;
char *t;
- pa_remap_t *m;
pa_assert(r);
-
- 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;
-
- m = &r->remap;
+ pa_assert(m);
n_oc = r->o_ss.channels;
n_ic = r->i_ss.channels;
pa_xfree(t);
/* initialize the remapping function */
- pa_init_remap(m);
+ pa_init_remap_func(m);
}
/* check if buf's memblock is large enough to hold 'len' bytes; create a
}
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. */
+ * in to_work_format_buf. The leftover data is already converted, so it's
+ * part of the output buffer. */
- if (!r->to_work_format_func || !input->length)
+ have_leftover = r->leftover_in_to_work;
+ r->leftover_in_to_work = false;
+
+ if (!have_leftover && (!r->to_work_format_func || !input->length))
return input;
+ else if (input->length <= 0)
+ return &r->to_work_format_buf;
+
+ in_n_samples = out_n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);
+
+ if (have_leftover) {
+ leftover_length = r->to_work_format_buf.length;
+ out_n_samples += (unsigned) (leftover_length / r->w_sz);
+ }
- n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);
- fit_buf(r, &r->to_work_format_buf, r->w_sz * n_samples, &r->to_work_format_buf_size, 0);
+ 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);
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);