Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial Implementation of LV2 module #983

Merged
merged 6 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ option(SWIG_PHP "Enable SWIG PHP bindings" OFF)
option(SWIG_PYTHON "Enable SWIG Python bindings" OFF)
option(SWIG_RUBY "Enable SWIG Ruby bindings" OFF)
option(SWIG_TCL "Enable SWIG Tcl bindings" OFF)
option(USE_LV2 "Enable LV2 features" ON)
ddennedy marked this conversation as resolved.
Show resolved Hide resolved

if(WIN32)
option(WINDOWS_DEPLOY "Install exes/libs directly to prefix (no subdir /bin)" ON)
Expand Down Expand Up @@ -250,6 +251,10 @@ if(MOD_JACKRACK)
list(APPEND MLT_SUPPORTED_COMPONENTS jackrack)
endif()

if(USE_LV2)
pkg_check_modules(lilv IMPORTED_TARGET lilv-0)
ddennedy marked this conversation as resolved.
Show resolved Hide resolved
endif()

if(MOD_KDENLIVE)
list(APPEND MLT_SUPPORTED_COMPONENTS kdenlive)
endif()
Expand Down Expand Up @@ -632,5 +637,6 @@ add_feature_info("SWIG: PHP" SWIG_PHP "")
add_feature_info("SWIG: Python" SWIG_PYTHON "")
add_feature_info("SWIG: Ruby" SWIG_RUBY "")
add_feature_info("SWIG: Tcl" SWIG_TCL "")
add_feature_info("lv2: LV2 Plugins support" USE_LV2 "")

feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
12 changes: 12 additions & 0 deletions src/modules/jackrack/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ if(TARGET JACK::JACK)
install(FILES consumer_jack.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack)
endif()

if(USE_LV2)
target_compile_definitions(mltjackrack PRIVATE WITH_LV2)
install(FILES filter_lv2.yml producer_lv2.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack)
endif()

if(GPL AND TARGET PkgConfig::xml AND TARGET PkgConfig::glib AND ladspa_h_FOUND)
target_sources(mltjackrack PRIVATE
jack_rack.c jack_rack.h
Expand All @@ -40,6 +45,13 @@ if(GPL AND TARGET PkgConfig::xml AND TARGET PkgConfig::glib AND ladspa_h_FOUND)
target_sources(mltjackrack PRIVATE filter_jackrack.c)
install(FILES filter_jackrack.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack)
endif()

if(USE_LV2 AND TARGET PkgConfig::lilv)
target_link_libraries(mltjackrack PRIVATE PkgConfig::lilv)
target_sources(mltjackrack PRIVATE filter_lv2.c producer_lv2.c lv2_context.c lv2_plugin.c lv2_process.c lv2_plugin_settings.c)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add the new header files to this as well as my IDE uses this to show the project files. You can see all of our CMakeLists.txt do that including other target_sources in the same file.

install(FILES filter_lv2.yml producer_lv2.yml DESTINATION ${MLT_INSTALL_DATA_DIR}/jackrack)
endif()

endif()

set_target_properties(mltjackrack PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${MLT_MODULE_OUTPUT_DIRECTORY}")
Expand Down
265 changes: 264 additions & 1 deletion src/modules/jackrack/factory.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* factory.c -- the factory method interfaces
* Copyright (C) 2003-2022 Meltytech, LLC
* Copyright (C) 2003-2024 Meltytech, LLC
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -49,8 +49,38 @@ extern mlt_producer producer_ladspa_init(mlt_profile profile,
const char *id,
char *arg);

#ifdef WITH_LV2

#include <lv2.h>

/* lv2 extenstions */
#include <lv2/atom/atom.h>
#include <lv2/midi/midi.h>
#include <lv2/port-groups/port-groups.h>
#include <lv2/port-props/port-props.h>
#include <lv2/presets/presets.h>
#include <lv2/resize-port/resize-port.h>
#include <lv2/ui/ui.h>
#include <lv2/worker/worker.h>

#include <lilv/lilv.h>

extern mlt_filter filter_lv2_init(mlt_profile profile,
mlt_service_type type,
const char *id,
char *arg);
extern mlt_producer producer_lv2_init(mlt_profile profile,
mlt_service_type type,
const char *id,
char *arg);
#endif

plugin_mgr_t *g_jackrack_plugin_mgr = NULL;

#ifdef WITH_LV2
lv2_mgr_t *g_lv2_plugin_mgr = NULL;
#endif

static void add_port_to_metadata(mlt_properties p, plugin_desc_t *desc, int j)
{
LADSPA_Data sample_rate = 48000;
Expand Down Expand Up @@ -211,6 +241,205 @@ static mlt_properties metadata(mlt_service_type type, const char *id, char *data
return result;
}

#ifdef WITH_LV2

static void lv2_add_port_to_metadata(mlt_properties p, lv2_plugin_desc_t *desc, int j)
{
LADSPA_PortRangeHintDescriptor hint_descriptor = desc->port_range_hints[j].HintDescriptor;

mlt_properties_set(p, "title", desc->port_names[j]);
if (LADSPA_IS_HINT_INTEGER(hint_descriptor)) {
mlt_properties_set(p, "type", "integer");
mlt_properties_set_int(p, "default", (int) desc->def_values[j]);
mlt_properties_set_double(p, "minimum", (int) desc->min_values[j]);
mlt_properties_set_double(p, "maximum", (int) desc->max_values[j]);
} else if (LADSPA_IS_HINT_TOGGLED(hint_descriptor)) {
mlt_properties_set(p, "type", "boolean");
mlt_properties_set_int(p, "default", desc->def_values[j]);
} else {
mlt_properties_set(p, "type", "float");
mlt_properties_set_double(p, "default", desc->def_values[j]);
mlt_properties_set_double(p, "minimum", desc->min_values[j]);
mlt_properties_set_double(p, "maximum", desc->max_values[j]);
}

if (LADSPA_IS_HINT_ENUMERATION(hint_descriptor)) {
mlt_properties_set(p, "type", "string");

char *str_ptr = strchr(desc->uri, '<');
while (str_ptr != NULL) {
*str_ptr++ = ':';
str_ptr = strchr(str_ptr, '<');
}

LilvNode* puri_temp = lilv_new_uri(g_lv2_plugin_mgr->lv2_world, desc->uri);

str_ptr = strchr(desc->uri, ':');
while (str_ptr != NULL) {
*str_ptr++ = '<';
str_ptr = strchr(str_ptr, ':');
}

const LilvPlugin* p_temp = lilv_plugins_get_by_uri(g_lv2_plugin_mgr->plugin_list, puri_temp);
const LilvPort *port_temp = lilv_plugin_get_port_by_index(p_temp, j);

lilv_node_free(puri_temp);

mlt_properties values_temp = mlt_properties_new();
mlt_properties_set_data(p,
"values",
values_temp,
0,
(mlt_destructor) mlt_properties_close,
NULL);

// Fill scalePoints Map
LilvScalePoints* sp = lilv_port_get_scale_points(p_temp, port_temp);
if (sp) {
LILV_FOREACH (scale_points, s, sp) {
const LilvScalePoint* p = lilv_scale_points_get(sp, s);
const LilvNode* val = lilv_scale_point_get_value(p);
if (!lilv_node_is_float(val) && !lilv_node_is_int(val)) {
continue;
}

const float f = lilv_node_as_float(val);


char key_temp[20];

if (lilv_node_is_float(val)) {
snprintf(key_temp, 20, "%f", f);
} else if (lilv_node_is_int(val)) {
snprintf(key_temp, 20, "%d", (int) f);
}

mlt_properties_set(values_temp, key_temp, lilv_node_as_string(lilv_scale_point_get_label(p)));

}

lilv_scale_points_free(sp);
}
}

if (LADSPA_IS_HINT_LOGARITHMIC(hint_descriptor))
mlt_properties_set(p, "scale", "log");
mlt_properties_set(p, "mutable", "yes");
mlt_properties_set(p, "animation", "yes");
}

static mlt_properties lv2_metadata(mlt_service_type type, const char *id, char *data)
{
char file[PATH_MAX];
if (type == mlt_service_filter_type) {
snprintf(file,
PATH_MAX,
"%s/lv2/%s",
mlt_environment("MLT_DATA"),
strncmp(id, "lv2.", 4) ? data : "filter_lv2.yml");
} else {
snprintf(file,
PATH_MAX,
"%s/lv2/%s",
mlt_environment("MLT_DATA"),
strncmp(id, "lv2.", 4) ? data : "producer_lv2.yml");
}
mlt_properties result = mlt_properties_parse_yaml(file);

if (!strncmp(id, "lv2.", 4)) {
// Annotate the yaml properties with lv2 control port info.
lv2_plugin_desc_t *desc = lv2_mgr_get_any_desc(g_lv2_plugin_mgr, (char *) &id[4]);

if (desc) {
mlt_properties params = mlt_properties_new();
mlt_properties p;
char key[20];
int i;

mlt_properties_set(result, "identifier", id);
mlt_properties_set(result, "title", desc->name);
mlt_properties_set(result, "creator", desc->maker ? desc->maker : "unknown");
mlt_properties_set(result, "description", "LV2 plugin");
mlt_properties_set_data(result,
"parameters",
params,
0,
(mlt_destructor) mlt_properties_close,
NULL);
for (i = 0; i < desc->control_port_count; i++) {
int j = desc->control_port_indicies[i];
p = mlt_properties_new();
snprintf(key, sizeof(key), "%d", mlt_properties_count(params));
mlt_properties_set_data(params,
key,
p,
0,
(mlt_destructor) mlt_properties_close,
NULL);
snprintf(key, sizeof(key), "%d", j);
mlt_properties_set(p, "identifier", key);
lv2_add_port_to_metadata(p, desc, j);
mlt_properties_set(p, "mutable", "yes");
}
for (i = 0; i < desc->status_port_count; i++) {
int j = desc->status_port_indicies[i];
p = mlt_properties_new();
snprintf(key, sizeof(key), "%d", mlt_properties_count(params));
mlt_properties_set_data(params,
key,
p,
0,
(mlt_destructor) mlt_properties_close,
NULL);
snprintf(key, sizeof(key), "%d[*]", j);
mlt_properties_set(p, "identifier", key);
lv2_add_port_to_metadata(p, desc, j);
mlt_properties_set(p, "readonly", "yes");
}

p = mlt_properties_new();
snprintf(key, sizeof(key), "%d", mlt_properties_count(params));
mlt_properties_set_data(params, key, p, 0, (mlt_destructor) mlt_properties_close, NULL);
mlt_properties_set(p, "identifier", "instances");
mlt_properties_set(p, "title", "Instances");
mlt_properties_set(p,
"description",
"The number of instances of the plugin that are in use.\n"
"MLT will create the number of plugins that are required "
"to support the number of audio channels.\n"
"Status parameters (readonly) are provided for each instance "
"and are accessed by specifying the instance number after the "
"identifier (starting at zero).\n"
"e.g. 9[0] provides the value of status 9 for the first instance.");
mlt_properties_set(p, "type", "integer");
mlt_properties_set(p, "readonly", "yes");

if (type == mlt_service_filter_type) {
p = mlt_properties_new();
snprintf(key, sizeof(key), "%d", mlt_properties_count(params));
mlt_properties_set_data(params,
key,
p,
0,
(mlt_destructor) mlt_properties_close,
NULL);
mlt_properties_set(p, "identifier", "wetness");
mlt_properties_set(p, "title", "Wet/Dry");
mlt_properties_set(p, "type", "float");
mlt_properties_set_double(p, "default", 1);
mlt_properties_set_double(p, "minimum", 0);
mlt_properties_set_double(p, "maximum", 1);
mlt_properties_set(p, "mutable", "yes");
mlt_properties_set(p, "animation", "yes");
}
}
}

return result;
}

#endif

MLT_REPOSITORY
{
#ifdef GPL
Expand All @@ -235,6 +464,40 @@ MLT_REPOSITORY
}
mlt_factory_register_for_clean_up(g_jackrack_plugin_mgr, (mlt_destructor) plugin_mgr_destroy);

#ifdef WITH_LV2
g_lv2_plugin_mgr = lv2_mgr_new();

char global_lv2_world[20];
snprintf (global_lv2_world, 20, "%p", g_lv2_plugin_mgr->lv2_world);
mlt_environment_set ("global_lv2_world", global_lv2_world);

for (list = g_lv2_plugin_mgr->all_plugins; list; list = g_slist_next(list)) {
lv2_plugin_desc_t *desc = (lv2_plugin_desc_t *) list->data;
char *s = NULL;
s = calloc(1, strlen("lv2.") + strlen(desc->uri) + 1);

sprintf(s, "lv2.%s", desc->uri);

char *str_ptr = strchr(s, ':');
while (str_ptr != NULL) {
*str_ptr++ = '<';
str_ptr = strchr(str_ptr, ':');
}

if (desc->has_input) {
MLT_REGISTER(mlt_service_filter_type, s, filter_lv2_init);
MLT_REGISTER_METADATA(mlt_service_filter_type, s, lv2_metadata, NULL);
} else {
MLT_REGISTER(mlt_service_producer_type, s, producer_lv2_init);
MLT_REGISTER_METADATA(mlt_service_producer_type, s, lv2_metadata, NULL);
}

if (s) {
free(s);
}
}
#endif

#ifdef WITH_JACK
MLT_REGISTER(mlt_service_filter_type, "jack", filter_jackrack_init);
MLT_REGISTER_METADATA(mlt_service_filter_type, "jack", metadata, "filter_jack.yml");
Expand Down
Loading
Loading