| /* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <dlfcn.h> |
| #include <syslog.h> |
| #include <ladspa.h> |
| |
| #include "cras_dsp_module.h" |
| |
| #define PLUGIN_PATH_PREFIX "/usr/lib/ladspa" |
| #define PLUGIN_PATH_MAX 256 |
| |
| struct ladspa_data { |
| void *dlopen_handle; /* the handle returned by dlopen() */ |
| const LADSPA_Descriptor *descriptor; |
| LADSPA_Handle *handle; /* returned by instantiate() */ |
| int activated; |
| }; |
| |
| static void activate(struct dsp_module *module) |
| { |
| struct ladspa_data *data = module->data; |
| const LADSPA_Descriptor *desc = data->descriptor; |
| data->activated = 1; |
| if (!desc->activate) |
| return; |
| desc->activate(data->handle); |
| } |
| |
| static void deactivate(struct dsp_module *module) |
| { |
| struct ladspa_data *data = module->data; |
| const LADSPA_Descriptor *desc = data->descriptor; |
| data->activated = 0; |
| if (!desc->deactivate) |
| return; |
| desc->deactivate(data->handle); |
| } |
| |
| static int instantiate(struct dsp_module *module, unsigned long sample_rate) |
| { |
| struct ladspa_data *data = module->data; |
| const LADSPA_Descriptor *desc = data->descriptor; |
| |
| data->handle = desc->instantiate(desc, sample_rate); |
| if (!data->handle) { |
| syslog(LOG_ERR, "instantiate failed for %s, rate %ld", |
| desc->Label, sample_rate); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static void deinstantiate(struct dsp_module *module) |
| { |
| struct ladspa_data *data = module->data; |
| const LADSPA_Descriptor *desc = data->descriptor; |
| |
| if (data->activated) |
| deactivate(module); |
| desc->cleanup(data->handle); |
| data->handle = NULL; |
| } |
| |
| static void connect_port(struct dsp_module *module, unsigned long port, |
| float *data_location) |
| { |
| struct ladspa_data *data = module->data; |
| const LADSPA_Descriptor *desc = data->descriptor; |
| desc->connect_port(data->handle, port, data_location); |
| } |
| |
| static int get_delay(struct dsp_module *module) |
| { |
| return 0; |
| } |
| |
| static void run(struct dsp_module *module, unsigned long sample_count) |
| { |
| struct ladspa_data *data = module->data; |
| const LADSPA_Descriptor *desc = data->descriptor; |
| |
| if (!data->activated) |
| activate(module); |
| desc->run(data->handle, sample_count); |
| } |
| |
| static void free_module(struct dsp_module *module) |
| { |
| struct ladspa_data *data = module->data; |
| if (data->activated) |
| deactivate(module); |
| if (data->dlopen_handle) { |
| dlclose(data->dlopen_handle); |
| data->dlopen_handle = NULL; |
| } |
| free(module->data); |
| free(module); |
| } |
| |
| static int get_properties(struct dsp_module *module) |
| { |
| struct ladspa_data *data = module->data; |
| int properties = 0; |
| if (LADSPA_IS_INPLACE_BROKEN(data->descriptor->Properties)) |
| properties |= MODULE_INPLACE_BROKEN; |
| return properties; |
| } |
| |
| static void dump(struct dsp_module *module, struct dumper *d) |
| { |
| struct ladspa_data *data = module->data; |
| const LADSPA_Descriptor *descriptor = data->descriptor; |
| |
| dumpf(d, " LADSPA: dlopen=%p, desc=%p, handle=%p, activated=%d\n", |
| data->dlopen_handle, data->descriptor, data->handle, |
| data->activated); |
| if (descriptor) { |
| dumpf(d, " Name=%s\n", descriptor->Name); |
| dumpf(d, " Maker=%s\n", descriptor->Maker); |
| } |
| } |
| |
| static int verify_plugin_descriptor(struct plugin *plugin, |
| const LADSPA_Descriptor *desc) |
| { |
| int i; |
| struct port *port; |
| |
| if (desc->PortCount != ARRAY_COUNT(&plugin->ports)) { |
| syslog(LOG_ERR, "port count mismatch: %s", plugin->title); |
| return -1; |
| } |
| |
| ARRAY_ELEMENT_FOREACH (&plugin->ports, i, port) { |
| LADSPA_PortDescriptor port_desc = desc->PortDescriptors[i]; |
| if ((port->direction == PORT_INPUT) != |
| !!(port_desc & LADSPA_PORT_INPUT)) { |
| syslog(LOG_ERR, "port direction mismatch: %s:%d!", |
| plugin->title, i); |
| return -1; |
| } |
| |
| if ((port->type == PORT_CONTROL) != |
| !!(port_desc & LADSPA_PORT_CONTROL)) { |
| syslog(LOG_ERR, "port type mismatch: %s:%d!", |
| plugin->title, i); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| struct dsp_module *cras_dsp_module_load_ladspa(struct plugin *plugin) |
| { |
| char path[PLUGIN_PATH_MAX]; |
| int index; |
| LADSPA_Descriptor_Function desc_func; |
| struct ladspa_data *data = calloc(1, sizeof(struct ladspa_data)); |
| struct dsp_module *module; |
| |
| snprintf(path, sizeof(path), "%s/%s", PLUGIN_PATH_PREFIX, |
| plugin->library); |
| |
| data->dlopen_handle = dlopen(path, RTLD_NOW); |
| if (!data->dlopen_handle) { |
| syslog(LOG_ERR, "cannot open plugin from %s: %s", path, |
| dlerror()); |
| goto bail; |
| } |
| |
| desc_func = (LADSPA_Descriptor_Function)dlsym(data->dlopen_handle, |
| "ladspa_descriptor"); |
| |
| if (!desc_func) { |
| syslog(LOG_ERR, "cannot find descriptor function from %s: %s", |
| path, dlerror()); |
| goto bail; |
| } |
| |
| for (index = 0;; index++) { |
| const LADSPA_Descriptor *desc = desc_func(index); |
| if (desc == NULL) { |
| syslog(LOG_ERR, "cannot find label %s from %s", |
| plugin->label, path); |
| goto bail; |
| } |
| if (strcmp(desc->Label, plugin->label) == 0) { |
| syslog(LOG_DEBUG, "plugin '%s' loaded from %s", |
| plugin->label, path); |
| if (verify_plugin_descriptor(plugin, desc) != 0) |
| goto bail; |
| data->descriptor = desc; |
| break; |
| } |
| } |
| |
| module = calloc(1, sizeof(struct dsp_module)); |
| module->data = data; |
| module->instantiate = &instantiate; |
| module->connect_port = &connect_port; |
| module->get_delay = &get_delay; |
| module->run = &run; |
| module->deinstantiate = &deinstantiate; |
| module->get_properties = &get_properties; |
| module->free_module = &free_module; |
| module->dump = &dump; |
| return module; |
| bail: |
| if (data->dlopen_handle) |
| dlclose(data->dlopen_handle); |
| free(data); |
| return NULL; |
| } |