mirror of
https://github.com/mpv-player/mpv
synced 2025-01-29 11:12:56 +00:00
af_ladspa: change options, use option parser
This commit is contained in:
parent
bcd8afc2ad
commit
adc843f984
@ -471,7 +471,7 @@ Available filters are:
|
||||
This filter can cause distortion with audio signals that have a very
|
||||
large dynamic range.
|
||||
|
||||
``ladspa=file:label[:controls...]``
|
||||
``ladspa=file:label:[<control0>,<control1>,...]``
|
||||
Load a LADSPA (Linux Audio Developer's Simple Plugin API) plugin. This
|
||||
filter is reentrant, so multiple LADSPA plugins can be used at once.
|
||||
|
||||
@ -487,12 +487,21 @@ Available filters are:
|
||||
one filter, but others contain many of them. Entering 'help' here
|
||||
will list all available filters within the specified library, which
|
||||
eliminates the use of 'listplugins' from the LADSPA SDK.
|
||||
``<controls>``
|
||||
Controls are zero or more floating point values that determine the
|
||||
behavior of the loaded plugin (for example delay, threshold or gain).
|
||||
``[<control0>,<control1>,...]``
|
||||
Controls are zero or more ``,`` separated floating point values that
|
||||
determine the behavior of the loaded plugin (for example delay,
|
||||
threshold or gain).
|
||||
In verbose mode (add ``-v`` to the mpv command line), all
|
||||
available controls and their valid ranges are printed. This eliminates
|
||||
the use of 'analyseplugin' from the LADSPA SDK.
|
||||
Note that ``,`` is already used by the option parser to separate
|
||||
filters, so you must quote the list of values with ``[...]`` or
|
||||
similar.
|
||||
|
||||
.. admonition:: Example
|
||||
|
||||
``mpv --af=ladspa='/usr/lib/ladspa/delay.so':delay_5s:[0.5,0.2] media.avi``
|
||||
Does something.
|
||||
|
||||
``karaoke``
|
||||
Simple voice removal filter exploiting the fact that voice is usually
|
||||
|
@ -63,6 +63,7 @@ typedef struct af_ladspa_s
|
||||
|
||||
char *file;
|
||||
char *label;
|
||||
char *controls;
|
||||
|
||||
char *myname; /**< It's easy to have a concatenation of file and label */
|
||||
|
||||
@ -100,12 +101,18 @@ static int af_ladspa_malloc_failed(char*);
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Description */
|
||||
|
||||
#define OPT_BASE_STRUCT af_ladspa_t
|
||||
struct af_info af_info_ladspa = {
|
||||
.info = "LADSPA plugin loader",
|
||||
.name = "ladspa",
|
||||
.open = af_open,
|
||||
.priv_size = sizeof(af_ladspa_t),
|
||||
.options = (const struct m_option[]) {
|
||||
OPT_STRING("file", file, 0),
|
||||
OPT_STRING("label", label, 0),
|
||||
OPT_STRING("controls", controls, 0),
|
||||
{0}
|
||||
},
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
@ -467,13 +474,6 @@ static int af_ladspa_malloc_failed(char *myname) {
|
||||
* Commands:
|
||||
* CONTROL_REINIT Sets the af structure with proper values for number
|
||||
* of channels, rate, format, et cetera.
|
||||
* CONTROL_COMMAND_LINE Parses the suboptions given to this filter
|
||||
* through arg. It first parses the filename and
|
||||
* the label. After that, it loads the filter
|
||||
* and finds out its proprties. Then in continues
|
||||
* parsing the controls given on the commandline,
|
||||
* if any are needed.
|
||||
*
|
||||
* \param af Audio filter instance
|
||||
* \param cmd The command to execute
|
||||
* \param arg Arguments to the command
|
||||
@ -483,8 +483,7 @@ static int af_ladspa_malloc_failed(char *myname) {
|
||||
*/
|
||||
|
||||
static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
af_ladspa_t *setup = (af_ladspa_t*) af->setup;
|
||||
float val;
|
||||
af_ladspa_t *setup = (af_ladspa_t*) af->priv;
|
||||
|
||||
switch(cmd) {
|
||||
case AF_CONTROL_REINIT:
|
||||
@ -498,138 +497,6 @@ static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
mp_audio_set_format(af->data, AF_FORMAT_FLOAT);
|
||||
|
||||
return af_test_output(af, (struct mp_audio*)arg);
|
||||
case AF_CONTROL_COMMAND_LINE: {
|
||||
char *buf;
|
||||
char *line = arg;
|
||||
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%s: parse suboptions\n", setup->myname);
|
||||
|
||||
/* suboption parser here!
|
||||
* format is (ladspa=)file:label:controls....
|
||||
*/
|
||||
|
||||
if (!line) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
|
||||
_("No suboptions specified."));
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
buf = malloc(strlen(line)+1);
|
||||
if (!buf) return af_ladspa_malloc_failed(setup->myname);
|
||||
|
||||
/* file... */
|
||||
buf[0] = '\0';
|
||||
sscanf(line, "%[^:]", buf);
|
||||
if (buf[0] == '\0') {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
|
||||
_("No library file specified."));
|
||||
free(buf);
|
||||
return AF_ERROR;
|
||||
}
|
||||
line += strlen(buf);
|
||||
setup->file = strdup(buf);
|
||||
if (!setup->file) {
|
||||
free(buf);
|
||||
return af_ladspa_malloc_failed(setup->myname);
|
||||
}
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%s: file --> %s\n", setup->myname,
|
||||
setup->file);
|
||||
if (*line != '\0') line++; /* read ':' */
|
||||
|
||||
/* label... */
|
||||
buf[0] = '\0';
|
||||
sscanf(line, "%[^:]", buf);
|
||||
if (buf[0] == '\0') {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
|
||||
_("No filter label specified."));
|
||||
free(buf);
|
||||
return AF_ERROR;
|
||||
}
|
||||
line += strlen(buf);
|
||||
setup->label = strdup(buf);
|
||||
if (!setup->label) {
|
||||
free(buf);
|
||||
return af_ladspa_malloc_failed(setup->myname);
|
||||
}
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%s: label --> %s\n", setup->myname,
|
||||
setup->label);
|
||||
/* if (*line != '0') line++; */ /* read ':' */
|
||||
|
||||
free(buf); /* no longer needed */
|
||||
|
||||
/* set new setup->myname */
|
||||
|
||||
free(setup->myname);
|
||||
setup->myname = calloc(strlen(af_info_ladspa.name)+strlen(setup->file)+
|
||||
strlen(setup->label)+6, 1);
|
||||
snprintf(setup->myname, strlen(af_info_ladspa.name)+
|
||||
strlen(setup->file)+strlen(setup->label)+6, "%s: (%s:%s)",
|
||||
af_info_ladspa.name, setup->file, setup->label);
|
||||
|
||||
/* load plugin :) */
|
||||
|
||||
if ( af_ladspa_load_plugin(setup) != AF_OK )
|
||||
return AF_ERROR;
|
||||
|
||||
/* see what inputs, outputs and controls this plugin has */
|
||||
if ( af_ladspa_parse_plugin(setup) != AF_OK )
|
||||
return AF_ERROR;
|
||||
|
||||
/* ninputcontrols is set by now, read control values from arg */
|
||||
|
||||
for (int i = 0; i < setup->ninputcontrols; i++) {
|
||||
if (!line || *line != ':') {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
|
||||
_("Not enough controls specified on the command line."));
|
||||
return AF_ERROR;
|
||||
}
|
||||
line++;
|
||||
if (sscanf(line, "%f", &val) != 1) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
|
||||
_("Not enough controls specified on the command line."));
|
||||
return AF_ERROR;
|
||||
}
|
||||
setup->inputcontrols[setup->inputcontrolsmap[i]] = val;
|
||||
line = strchr(line, ':');
|
||||
}
|
||||
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%s: input controls: ", setup->myname);
|
||||
for (int i = 0; i < setup->ninputcontrols; i++) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%0.4f ",
|
||||
setup->inputcontrols[setup->inputcontrolsmap[i]]);
|
||||
}
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "\n");
|
||||
|
||||
/* check boundaries of inputcontrols */
|
||||
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%s: checking boundaries of input controls\n",
|
||||
setup->myname);
|
||||
for (int i = 0; i < setup->ninputcontrols; i++) {
|
||||
int p = setup->inputcontrolsmap[i];
|
||||
LADSPA_PortRangeHint hint =
|
||||
setup->plugin_descriptor->PortRangeHints[p];
|
||||
val = setup->inputcontrols[p];
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) &&
|
||||
val < hint.LowerBound) {
|
||||
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "%s: Input control #%d is below lower boundary of %0.4f.\n",
|
||||
setup->myname, i, hint.LowerBound);
|
||||
return AF_ERROR;
|
||||
}
|
||||
if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor) &&
|
||||
val > hint.UpperBound) {
|
||||
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "%s: Input control #%d is above upper boundary of %0.4f.\n",
|
||||
setup->myname, i, hint.UpperBound);
|
||||
return AF_ERROR;
|
||||
}
|
||||
}
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%s: all controls have sane values\n",
|
||||
setup->myname);
|
||||
|
||||
/* All is well! */
|
||||
setup->status = AF_OK;
|
||||
|
||||
return AF_OK; }
|
||||
}
|
||||
|
||||
return AF_UNKNOWN;
|
||||
@ -646,8 +513,8 @@ static int control(struct af_instance *af, int cmd, void *arg) {
|
||||
*/
|
||||
|
||||
static void uninit(struct af_instance *af) {
|
||||
if (af->setup) {
|
||||
af_ladspa_t *setup = (af_ladspa_t*) af->setup;
|
||||
if (af->priv) {
|
||||
af_ladspa_t *setup = (af_ladspa_t*) af->priv;
|
||||
const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
|
||||
|
||||
if (setup->myname) {
|
||||
@ -663,8 +530,6 @@ static void uninit(struct af_instance *af) {
|
||||
free(setup->chhandles);
|
||||
}
|
||||
|
||||
free(setup->file);
|
||||
free(setup->label);
|
||||
free(setup->inputcontrolsmap);
|
||||
free(setup->inputcontrols);
|
||||
free(setup->outputcontrolsmap);
|
||||
@ -686,9 +551,6 @@ static void uninit(struct af_instance *af) {
|
||||
|
||||
if (setup->libhandle)
|
||||
dlclose(setup->libhandle);
|
||||
|
||||
free(setup);
|
||||
setup = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -703,7 +565,7 @@ static void uninit(struct af_instance *af) {
|
||||
*/
|
||||
|
||||
static struct mp_audio* play(struct af_instance *af, struct mp_audio *data) {
|
||||
af_ladspa_t *setup = af->setup;
|
||||
af_ladspa_t *setup = af->priv;
|
||||
const LADSPA_Descriptor *pdes = setup->plugin_descriptor;
|
||||
float *audio = (float*)data->planes[0];
|
||||
int nsamples = data->samples*data->nch;
|
||||
@ -876,20 +738,103 @@ static int af_open(struct af_instance *af) {
|
||||
af->uninit=uninit;
|
||||
af->play=play;
|
||||
|
||||
af->setup = calloc(1, sizeof(af_ladspa_t));
|
||||
if (af->setup == NULL) {
|
||||
return af_ladspa_malloc_failed((char*)af_info_ladspa.name);
|
||||
}
|
||||
af_ladspa_t *setup = af->priv;
|
||||
|
||||
((af_ladspa_t*)af->setup)->status = AF_ERROR; /* will be set to AF_OK if
|
||||
setup->status = AF_ERROR; /* will be set to AF_OK if
|
||||
* all went OK and play()
|
||||
* should proceed.
|
||||
*/
|
||||
|
||||
((af_ladspa_t*)af->setup)->myname = strdup(af_info_ladspa.name);
|
||||
if (!((af_ladspa_t*)af->setup)->myname)
|
||||
setup->myname = strdup(af_info_ladspa.name);
|
||||
if (!setup->myname)
|
||||
return af_ladspa_malloc_failed((char*)af_info_ladspa.name);
|
||||
|
||||
if (!setup->file || !setup->file[0]) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
|
||||
_("No library file specified."));
|
||||
uninit(af);
|
||||
return AF_ERROR;
|
||||
}
|
||||
if (!setup->label || !setup->label[0]) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
|
||||
_("No filter label specified."));
|
||||
uninit(af);
|
||||
return AF_ERROR;
|
||||
}
|
||||
|
||||
free(setup->myname);
|
||||
setup->myname = calloc(strlen(af_info_ladspa.name)+strlen(setup->file)+
|
||||
strlen(setup->label)+6, 1);
|
||||
snprintf(setup->myname, strlen(af_info_ladspa.name)+
|
||||
strlen(setup->file)+strlen(setup->label)+6, "%s: (%s:%s)",
|
||||
af_info_ladspa.name, setup->file, setup->label);
|
||||
|
||||
/* load plugin :) */
|
||||
|
||||
if ( af_ladspa_load_plugin(setup) != AF_OK )
|
||||
return AF_ERROR;
|
||||
|
||||
/* see what inputs, outputs and controls this plugin has */
|
||||
if ( af_ladspa_parse_plugin(setup) != AF_OK )
|
||||
return AF_ERROR;
|
||||
|
||||
/* ninputcontrols is set by now, read control values from arg */
|
||||
|
||||
float val;
|
||||
char *line = setup->controls;
|
||||
for (int i = 0; i < setup->ninputcontrols; i++) {
|
||||
if (!line || (i != 0 && *line != ',')) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
|
||||
_("Not enough controls specified on the command line."));
|
||||
return AF_ERROR;
|
||||
}
|
||||
if (i != 0)
|
||||
line++;
|
||||
if (sscanf(line, "%f", &val) != 1) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_ERR, "%s: %s\n", setup->myname,
|
||||
_("Not enough controls specified on the command line."));
|
||||
return AF_ERROR;
|
||||
}
|
||||
setup->inputcontrols[setup->inputcontrolsmap[i]] = val;
|
||||
line = strchr(line, ',');
|
||||
}
|
||||
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%s: input controls: ", setup->myname);
|
||||
for (int i = 0; i < setup->ninputcontrols; i++) {
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%0.4f ",
|
||||
setup->inputcontrols[setup->inputcontrolsmap[i]]);
|
||||
}
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "\n");
|
||||
|
||||
/* check boundaries of inputcontrols */
|
||||
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%s: checking boundaries of input controls\n",
|
||||
setup->myname);
|
||||
for (int i = 0; i < setup->ninputcontrols; i++) {
|
||||
int p = setup->inputcontrolsmap[i];
|
||||
LADSPA_PortRangeHint hint =
|
||||
setup->plugin_descriptor->PortRangeHints[p];
|
||||
val = setup->inputcontrols[p];
|
||||
|
||||
if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) &&
|
||||
val < hint.LowerBound) {
|
||||
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "%s: Input control #%d is below lower boundary of %0.4f.\n",
|
||||
setup->myname, i, hint.LowerBound);
|
||||
return AF_ERROR;
|
||||
}
|
||||
if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor) &&
|
||||
val > hint.UpperBound) {
|
||||
mp_tmsg(MSGT_AFILTER, MSGL_ERR, "%s: Input control #%d is above upper boundary of %0.4f.\n",
|
||||
setup->myname, i, hint.UpperBound);
|
||||
return AF_ERROR;
|
||||
}
|
||||
}
|
||||
mp_msg(MSGT_AFILTER, MSGL_V, "%s: all controls have sane values\n",
|
||||
setup->myname);
|
||||
|
||||
/* All is well! */
|
||||
setup->status = AF_OK;
|
||||
|
||||
return AF_OK;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user