[pulseaudio-commits] 3 commits - src/pulsecore
Tanu Kaskinen
tanuk at kemper.freedesktop.org
Tue Jan 28 13:20:00 PST 2014
src/pulsecore/resampler.c | 139 ++++++++++++++++++++++++++++------------------
1 file changed, 85 insertions(+), 54 deletions(-)
New commits:
commit 537f430c993ee5d83a8c58e8121b457c904d6cdb
Author: Peter Meerwald <p.meerwald at bct-electronic.com>
Date: Mon Dec 2 17:27:01 2013 +0100
resampler: Resample first followed by remapping if have more out channels than in channels
Reintroduces a cleaned-up version of commit 30ce3a14e5ae1cd316a18bec95b831c07ac57a1a which
was reverted by 1ce71cbd8206d1be59ac62274ad83cdbe693a96a; for more information see
http://thread.gmane.org/gmane.comp.audio.pulseaudio.general/17479/focus=17487
The patch intends to reduce computational load when resampling AND remapping. The PA
resampler performs the following steps:
sample format conversion -> remapping -> resampling -> sample format conversion
In case the number of output channels is higher than the number of input channels, the
resampler has to be run more often than necessary. E.g. in case of mono to 4-channel remapping,
the resampler runs on 4 channels separately.
To Ãmprove this, the PA resampler pipeline is made adaptive:
if out-channels <= in-channels:
sample format conversion -> remapping -> resampling -> sample format conversion
if out-channels > in-channels:
sample format conversion -> resampling -> remapping -> sample format conversion
Signed-off-by: Peter Meerwald <p.meerwald at bct-electronic.com>
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index db65cea..38389f3 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -398,12 +398,7 @@ pa_resampler* pa_resampler_new(
calc_map_table(r);
- pa_log_info("Using resampler '%s'", pa_resample_method_to_string(method));
-
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) {
@@ -428,14 +423,31 @@ pa_resampler* pa_resampler_new(
}
}
- /* 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;
+
+ /* 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;
- r->work_channels = r->o_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;
@@ -1348,8 +1360,16 @@ void pa_resampler_run(pa_resampler *r, const pa_memchunk *in, pa_memchunk *out)
buf = (pa_memchunk*) in;
buf = convert_to_work_format(r, buf);
- buf = remap_channels(r, buf);
- buf = resample(r, buf);
+
+ /* Try to save resampling effort: if we have more output channels than
+ * input channels, do resampling first, then remapping. */
+ if (r->o_ss.channels <= r->i_ss.channels) {
+ buf = remap_channels(r, buf);
+ buf = resample(r, buf);
+ } else {
+ buf = resample(r, buf);
+ buf = remap_channels(r, buf);
+ }
if (buf->length) {
buf = convert_from_work_format(r, buf);
commit b51d28bbbf0b544789be7fe6b3d2a148cf0a500a
Author: Peter Meerwald <p.meerwald at bct-electronic.com>
Date: Mon Dec 2 17:27:00 2013 +0100
resampler: Implement leftover handling in convert_to_work_format()
Signed-off-by: Peter Meerwald <pmeerw at pmeerw.net>
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index 4c788dc..db65cea 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -65,13 +65,14 @@ struct pa_resampler {
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;
@@ -1169,26 +1170,43 @@ static void fit_buf(pa_resampler *r, pa_memchunk *buf, size_t len, size_t *size,
}
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. */
+
+ have_leftover = r->leftover_in_to_work;
+ r->leftover_in_to_work = false;
- if (!r->to_work_format_func || !input->length)
+ 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);
- 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);
+ 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);
commit 5ad310f4f693994a1628fd3b746d4519aae911c8
Author: Peter Meerwald <p.meerwald at bct-electronic.com>
Date: Mon Dec 2 17:26:59 2013 +0100
resampler: Extend fit_buf() helper to copy leftover data to new buffer
the patch changes the interface of the (internal) fit_buf() function:
fit_buf() manages the memblock of the buf chunk, it reallocates the memblock
if the requested number of bytes ('len') if larger than the memblock's size ('size')
and optionally preserves 'copy' bytes
the code should be in line with the comment now
Signed-off-by: Peter Meerwald <pmeerw at pmeerw.net>
diff --git a/src/pulsecore/resampler.c b/src/pulsecore/resampler.c
index f0c663f..4c788dc 100644
--- a/src/pulsecore/resampler.c
+++ b/src/pulsecore/resampler.c
@@ -1140,15 +1140,32 @@ static void calc_map_table(pa_resampler *r) {
pa_init_remap(m);
}
-static size_t fit_buf(pa_resampler *r, pa_memchunk *buf, size_t size) {
- if (!buf->memblock || size < buf->length) {
- size = buf->length;
- if (buf->memblock)
+/* 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 = pa_memblock_new(r->mempool, size);
+ buf->memblock = new_block;
+ *size = len;
}
- return size;
+
+ buf->length = len;
}
static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input) {
@@ -1166,9 +1183,7 @@ static pa_memchunk* convert_to_work_format(pa_resampler *r, pa_memchunk *input)
return input;
n_samples = (unsigned) ((input->length / r->i_fz) * r->i_ss.channels);
-
- r->to_work_format_buf.length = r->w_sz * n_samples;
- r->to_work_format_buf_size = fit_buf(r, &r->to_work_format_buf, r->to_work_format_buf_size);
+ fit_buf(r, &r->to_work_format_buf, r->w_sz * n_samples, &r->to_work_format_buf_size, 0);
src = pa_memblock_acquire_chunk(input);
dst = pa_memblock_acquire(r->to_work_format_buf.memblock);
@@ -1212,24 +1227,7 @@ static pa_memchunk *remap_channels(pa_resampler *r, pa_memchunk *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
- r->remap_buf_size = fit_buf(r, &r->remap_buf, r->remap_buf_size);
+ 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;
@@ -1257,8 +1255,7 @@ static void save_leftover(pa_resampler *r, void *buf, size_t len) {
pa_assert(len > 0);
/* Store the leftover data. */
- r->leftover_buf->length = len;
- *r->leftover_buf_size = fit_buf(r, r->leftover_buf, *r->leftover_buf_size);
+ fit_buf(r, r->leftover_buf, len, r->leftover_buf_size, 0);
*r->have_leftover = true;
dst = pa_memblock_acquire(r->leftover_buf->memblock);
@@ -1280,9 +1277,7 @@ static pa_memchunk *resample(pa_resampler *r, pa_memchunk *input) {
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;
-
- r->resample_buf.length = r->w_fz * out_n_frames;
- r->resample_buf_size = fit_buf(r, &r->resample_buf, r->resample_buf_size);
+ fit_buf(r, &r->resample_buf, r->w_fz * out_n_frames, &r->resample_buf_size, 0);
leftover_n_frames = r->impl.resample(r, input, in_n_frames, &r->resample_buf, &out_n_frames);
@@ -1312,9 +1307,7 @@ static pa_memchunk *convert_from_work_format(pa_resampler *r, pa_memchunk *input
n_samples = (unsigned) (input->length / r->w_sz);
n_frames = n_samples / r->o_ss.channels;
-
- r->from_work_format_buf.length = r->o_fz * n_frames;
- r->from_work_format_buf_size = fit_buf(r, &r->from_work_format_buf, r->from_work_format_buf_size);
+ 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);
More information about the pulseaudio-commits
mailing list