From bbaf60f30f85b0aab01ba7f292f2bc533d9dab43 Mon Sep 17 00:00:00 2001 From: Sami Boukortt Date: Tue, 19 May 2015 23:38:00 +0200 Subject: [PATCH] Fix multi-channel LADSPA effects, and draining for all LADSPA effects Previously, draining was incomplete for LADSPA effects with a latency longer than either the buffer size or the input track (and the output was therefore shorter than the input), and LADSPA effects with more output channels than input channels would cause an outright crash. --- src/ladspa.c | 132 +++++++++++++++++++++++++-------------------------- 1 file changed, 65 insertions(+), 67 deletions(-) diff --git a/src/ladspa.c b/src/ladspa.c index 374e2e5b..95a311d1 100644 --- a/src/ladspa.c +++ b/src/ladspa.c @@ -325,83 +325,77 @@ static int sox_ladspa_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sa size_t *isamp, size_t *osamp) { priv_t * l_st = (priv_t *)effp->priv; - size_t i, len = min(*isamp, *osamp); + size_t i; size_t j; size_t h; const size_t total_input_count = l_st->input_count * l_st->handle_count; const size_t total_output_count = l_st->output_count * l_st->handle_count; - const size_t input_len = len / total_input_count; - size_t output_len = len / total_output_count; + const size_t channel_len = min(*isamp / total_input_count, *osamp / total_output_count + l_st->in_latency); - if (total_output_count < total_input_count) - output_len = input_len; + LADSPA_Data *buf = lsx_calloc(channel_len * total_input_count, sizeof(LADSPA_Data)); + LADSPA_Data *outbuf = lsx_calloc(channel_len * total_output_count, sizeof(LADSPA_Data)); + LADSPA_Handle handle; + unsigned long port, l; + SOX_SAMPLE_LOCALS; - *isamp = len; + *isamp = channel_len * total_input_count; *osamp = 0; - if (len) { - LADSPA_Data *buf = lsx_calloc(len, sizeof(LADSPA_Data)); - LADSPA_Data *outbuf = lsx_calloc(len, sizeof(LADSPA_Data)); - LADSPA_Handle handle; - unsigned long port, l; - SOX_SAMPLE_LOCALS; - - /* - * prepare buffer for LADSPA input - * deinterleave sox samples and write non-interleaved data to - * input_port-specific buffer locations - */ - for (i = 0; i < input_len; i++) { - for (j = 0; j < total_input_count; j++) { - const sox_sample_t s = *ibuf++; - buf[j * input_len + i] = SOX_SAMPLE_TO_LADSPA_DATA(s, effp->clips); - } - } - - /* Connect the LADSPA input port(s) to the prepared buffers */ + /* + * prepare buffer for LADSPA input + * deinterleave sox samples and write non-interleaved data to + * input_port-specific buffer locations + */ + for (i = 0; i < channel_len; i++) { for (j = 0; j < total_input_count; j++) { - handle = l_st->handles[j / l_st->input_count]; - port = l_st->inputs[j / l_st->handle_count]; - l_st->desc->connect_port(handle, port, buf + j * input_len); + const sox_sample_t s = *ibuf++; + buf[j * channel_len + i] = SOX_SAMPLE_TO_LADSPA_DATA(s, effp->clips); } + } - /* Connect the LADSPA output port(s) if used */ - for (j = 0; j < total_output_count; j++) { - handle = l_st->handles[j / l_st->output_count]; - port = l_st->outputs[j / l_st->handle_count]; - l_st->desc->connect_port(handle, port, outbuf + j * output_len); - } + /* Connect the LADSPA input port(s) to the prepared buffers */ + for (j = 0; j < total_input_count; j++) { + handle = l_st->handles[j / l_st->input_count]; + port = l_st->inputs[j / l_st->handle_count]; + l_st->desc->connect_port(handle, port, buf + j * channel_len); + } - /* Run the plugin for each handle */ - for (h = 0; h < l_st->handle_count; h++) - l_st->desc->run(l_st->handles[h], input_len); + /* Connect the LADSPA output port(s) if used */ + for (j = 0; j < total_output_count; j++) { + handle = l_st->handles[j / l_st->output_count]; + port = l_st->outputs[j / l_st->handle_count]; + l_st->desc->connect_port(handle, port, outbuf + j * channel_len); + } - /* check the latency control port if we have one */ - if (l_st->latency_control_port) { - lsx_debug("latency detected is %g", *l_st->latency_control_port); - l_st->in_latency = (unsigned long)floor(*l_st->latency_control_port); + /* Run the plugin for each handle */ + for (h = 0; h < l_st->handle_count; h++) + l_st->desc->run(l_st->handles[h], channel_len); - /* we will need this later in sox_ladspa_drain */ - l_st->out_latency = l_st->in_latency; + /* check the latency control port if we have one */ + if (l_st->latency_control_port) { + lsx_debug("latency detected is %g", *l_st->latency_control_port); + l_st->in_latency = (unsigned long)floor(*l_st->latency_control_port); - /* latency for plugins is constant, only compensate once */ - l_st->latency_control_port = NULL; - } + /* we will need this later in sox_ladspa_drain */ + l_st->out_latency = l_st->in_latency; - /* Grab output if effect produces it, re-interleaving it */ - l = min(output_len, l_st->in_latency); - for (i = l; i < output_len; i++) { - for (j = 0; j < total_output_count; j++) { - LADSPA_Data d = outbuf[j * output_len + i]; - *obuf++ = LADSPA_DATA_TO_SOX_SAMPLE(d, effp->clips); - (*osamp)++; - } - } - l_st->in_latency -= l; + /* latency for plugins is constant, only compensate once */ + l_st->latency_control_port = NULL; + } - free(outbuf); - free(buf); + /* Grab output if effect produces it, re-interleaving it */ + l = min(channel_len, l_st->in_latency); + for (i = l; i < channel_len; i++) { + for (j = 0; j < total_output_count; j++) { + LADSPA_Data d = outbuf[j * channel_len + i]; + *obuf++ = LADSPA_DATA_TO_SOX_SAMPLE(d, effp->clips); + (*osamp)++; + } } + l_st->in_latency -= l; + + free(outbuf); + free(buf); return SOX_SUCCESS; } @@ -413,7 +407,7 @@ static int sox_ladspa_flow(sox_effect_t * effp, const sox_sample_t *ibuf, sox_sa static int sox_ladspa_drain(sox_effect_t * effp, sox_sample_t *obuf, size_t *osamp) { priv_t * l_st = (priv_t *)effp->priv; - sox_sample_t *ibuf, *dbuf; + sox_sample_t *ibuf; size_t isamp, dsamp; int r; @@ -424,18 +418,22 @@ static int sox_ladspa_drain(sox_effect_t * effp, sox_sample_t *obuf, size_t *osa /* feed some silence at the end to push the rest of the data out */ isamp = l_st->out_latency * effp->in_signal.channels; - dsamp = l_st->out_latency * effp->out_signal.channels; ibuf = lsx_calloc(isamp, sizeof(sox_sample_t)); - dbuf = lsx_calloc(dsamp, sizeof(sox_sample_t)); - r = sox_ladspa_flow(effp, ibuf, dbuf, &isamp, &dsamp); - *osamp = min(dsamp, *osamp); - memcpy(obuf, dbuf, *osamp * sizeof(sox_sample_t)); + do { + dsamp = min(l_st->out_latency * effp->out_signal.channels, + *osamp - (*osamp % effp->out_signal.channels)); + isamp = l_st->out_latency * effp->in_signal.channels; + r = sox_ladspa_flow(effp, ibuf, obuf, &isamp, &dsamp); + } + while (r == SOX_SUCCESS && dsamp == 0); + + *osamp = dsamp; + l_st->out_latency -= *osamp / effp->out_signal.channels; free(ibuf); - free(dbuf); - return r == SOX_SUCCESS ? SOX_EOF : 0; + return r == SOX_SUCCESS ? SOX_SUCCESS : SOX_EOF; } /*