diff options
Diffstat (limited to 'src/jalv.c')
-rw-r--r-- | src/jalv.c | 428 |
1 files changed, 224 insertions, 204 deletions
@@ -688,6 +688,10 @@ jalv_run(Jalv* jalv, uint32_t nframes) bool jalv_update(Jalv* jalv) { + if (!jalv->plugin) { + return true; + } + /* Check quit flag and close if set. */ if (zix_sem_try_wait(&exit_sem)) { jalv_close_ui(jalv); @@ -759,6 +763,217 @@ jalv_apply_control_arg(Jalv* jalv, const char* s) return true; } +int +jalv_load_plugin(Jalv* jalv, const LilvNode* plugin_uri, LilvState* state) +{ + LilvWorld* world = jalv->world; + const LilvPlugins* plugins = lilv_world_get_all_plugins(world); + + /* Find plugin */ + printf("Plugin: %s\n", lilv_node_as_string(plugin_uri)); + jalv->plugin = lilv_plugins_get_by_uri(plugins, plugin_uri); + /* lilv_node_free(plugin_uri); */ + if (!jalv->plugin) { + fprintf(stderr, "Failed to find plugin\n"); + lilv_world_free(world); + return EXIT_FAILURE; + } + + /* Load preset, if specified */ + if (jalv->opts.preset) { + LilvNode* preset = lilv_new_uri(jalv->world, jalv->opts.preset); + + jalv_load_presets(jalv, NULL, NULL); + state = lilv_state_new_from_world(jalv->world, &jalv->map, preset); + jalv->preset = state; + lilv_node_free(preset); + if (!state) { + fprintf(stderr, "Failed to find preset <%s>\n", jalv->opts.preset); + lilv_world_free(world); + return EXIT_FAILURE; + } + } + + /* Check that any required features are supported */ + LilvNodes* req_feats = lilv_plugin_get_required_features(jalv->plugin); + LILV_FOREACH(nodes, f, req_feats) { + const char* uri = lilv_node_as_uri(lilv_nodes_get(req_feats, f)); + if (!feature_is_supported(uri)) { + fprintf(stderr, "Feature %s is not supported\n", uri); + lilv_world_free(world); + return EXIT_FAILURE; + } + } + lilv_nodes_free(req_feats); + + /* Check for thread-safe state restore() method. */ + LilvNode* state_threadSafeRestore = lilv_new_uri( + jalv->world, LV2_STATE__threadSafeRestore); + if (lilv_plugin_has_feature(jalv->plugin, state_threadSafeRestore)) { + jalv->safe_restore = true; + } + lilv_node_free(state_threadSafeRestore); + + if (!state) { + /* Not restoring state, load the plugin as a preset to get default */ + state = lilv_state_new_from_world( + jalv->world, &jalv->map, lilv_plugin_get_uri(jalv->plugin)); + } + + /* Get a plugin UI */ + const char* native_ui_type_uri = jalv_native_ui_type(jalv); + jalv->uis = lilv_plugin_get_uis(jalv->plugin); + if (!jalv->opts.generic_ui && native_ui_type_uri) { + const LilvNode* native_ui_type = lilv_new_uri(jalv->world, native_ui_type_uri); + LILV_FOREACH(uis, u, jalv->uis) { + const LilvUI* this_ui = lilv_uis_get(jalv->uis, u); + if (lilv_ui_is_supported(this_ui, + suil_ui_supported, + native_ui_type, + &jalv->ui_type)) { + /* TODO: Multiple UI support */ + jalv->ui = this_ui; + break; + } + } + } else if (!jalv->opts.generic_ui && jalv->opts.show_ui) { + jalv->ui = lilv_uis_get(jalv->uis, lilv_uis_begin(jalv->uis)); + } + + /* Create ringbuffers for UI if necessary */ + if (jalv->ui) { + fprintf(stderr, "UI: %s\n", + lilv_node_as_uri(lilv_ui_get_uri(jalv->ui))); + } else { + fprintf(stderr, "UI: None\n"); + } + + /* Create port and control structures */ + jalv_create_ports(jalv); + jalv_create_controls(jalv, true); + jalv_create_controls(jalv, false); + + if (!(jalv->backend = jalv_backend_init(jalv))) { + die("Failed to connect to audio system"); + } + + printf("Sample rate: %u Hz\n", jalv->sample_rate); + printf("Block length: %u frames\n", jalv->block_length); + printf("MIDI buffers: %zu bytes\n", jalv->midi_buf_size); + + if (jalv->opts.buffer_size == 0) { + /* The UI ring is fed by plugin output ports (usually one), and the UI + updates roughly once per cycle. The ring size is a few times the + size of the MIDI output to give the UI a chance to keep up. The UI + should be able to keep up with 4 cycles, and tests show this works + for me, but this value might need increasing to avoid overflows. + */ + jalv->opts.buffer_size = jalv->midi_buf_size * N_BUFFER_CYCLES; + } + + if (jalv->opts.update_rate == 0.0) { + /* Calculate a reasonable UI update frequency. */ + jalv->ui_update_hz = (float)jalv->sample_rate / jalv->midi_buf_size * 2.0f; + jalv->ui_update_hz = MAX(25.0f, jalv->ui_update_hz); + } else { + /* Use user-specified UI update rate. */ + jalv->ui_update_hz = jalv->opts.update_rate; + jalv->ui_update_hz = MAX(1.0f, jalv->ui_update_hz); + } + + /* The UI can only go so fast, clamp to reasonable limits */ + jalv->ui_update_hz = MIN(60, jalv->ui_update_hz); + jalv->opts.buffer_size = MAX(4096, jalv->opts.buffer_size); + fprintf(stderr, "Comm buffers: %d bytes\n", jalv->opts.buffer_size); + fprintf(stderr, "Update rate: %.01f Hz\n", jalv->ui_update_hz); + + /* Build options array to pass to plugin */ + const LV2_Options_Option options[] = { + { LV2_OPTIONS_INSTANCE, 0, jalv->urids.param_sampleRate, + sizeof(float), jalv->urids.atom_Float, &jalv->sample_rate }, + { LV2_OPTIONS_INSTANCE, 0, jalv->urids.bufsz_minBlockLength, + sizeof(int32_t), jalv->urids.atom_Int, &jalv->block_length }, + { LV2_OPTIONS_INSTANCE, 0, jalv->urids.bufsz_maxBlockLength, + sizeof(int32_t), jalv->urids.atom_Int, &jalv->block_length }, + { LV2_OPTIONS_INSTANCE, 0, jalv->urids.bufsz_sequenceSize, + sizeof(int32_t), jalv->urids.atom_Int, &jalv->midi_buf_size }, + { LV2_OPTIONS_INSTANCE, 0, jalv->urids.ui_updateRate, + sizeof(float), jalv->urids.atom_Float, &jalv->ui_update_hz }, + { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL } + }; + + options_feature.data = (void*)&options; + + /* Create Plugin <=> UI communication buffers */ + jalv->ui_events = zix_ring_new(jalv->opts.buffer_size); + jalv->plugin_events = zix_ring_new(jalv->opts.buffer_size); + zix_ring_mlock(jalv->ui_events); + zix_ring_mlock(jalv->plugin_events); + + /* Instantiate the plugin */ + jalv->instance = lilv_plugin_instantiate( + jalv->plugin, jalv->sample_rate, features); + if (!jalv->instance) { + die("Failed to instantiate plugin.\n"); + } + + ext_data.data_access = lilv_instance_get_descriptor(jalv->instance)->extension_data; + + fprintf(stderr, "\n"); + if (!jalv->buf_size_set) { + jalv_allocate_port_buffers(jalv); + } + + /* Create workers if necessary */ + if (lilv_plugin_has_feature(jalv->plugin, jalv->nodes.work_schedule) + && lilv_plugin_has_extension_data(jalv->plugin, jalv->nodes.work_interface)) { + const LV2_Worker_Interface* iface = (const LV2_Worker_Interface*) + lilv_instance_get_extension_data(jalv->instance, LV2_WORKER__interface); + + jalv_worker_init(jalv, &jalv->worker, iface, true); + if (jalv->safe_restore) { + jalv_worker_init(jalv, &jalv->state_worker, iface, false); + } + } + + /* Apply loaded state to plugin instance if necessary */ + if (state) { + jalv_apply_state(jalv, state); + } + + if (jalv->opts.controls) { + for (char** c = jalv->opts.controls; *c; ++c) { + jalv_apply_control_arg(jalv, *c); + } + } + + /* Set Jack callbacks */ + jalv_backend_init(jalv); + + /* Create Jack ports and connect plugin ports to buffers */ + for (uint32_t i = 0; i < jalv->num_ports; ++i) { + jalv_backend_activate_port(jalv, i); + } + + /* Print initial control values */ + for (size_t i = 0; i < jalv->controls.n_controls; ++i) { + ControlID* control = jalv->controls.controls[i]; + if (control->type == PORT) {// && control->value_type == jalv->forge.Float) { + struct Port* port = &jalv->ports[control->index]; + print_control_value(jalv, port, port->control); + } + } + + /* Activate plugin */ + lilv_instance_activate(jalv->instance); + + /* Activate audio backend */ + jalv_backend_activate(jalv); + jalv->play_state = JALV_RUNNING; + + return 0; +} + static void signal_handler(int ignored) { @@ -884,7 +1099,6 @@ main(int argc, char** argv) LilvWorld* world = lilv_world_new(); lilv_world_load_all(world); jalv.world = world; - const LilvPlugins* plugins = lilv_world_get_all_plugins(world); /* Cache URIs for concepts we'll use */ jalv.nodes.atom_AtomPort = lilv_new_uri(world, LV2_ATOM__AtomPort); @@ -948,216 +1162,22 @@ main(int argc, char** argv) plugin_uri = lilv_new_uri(world, argv[argc - 1]); } - if (!plugin_uri) { - fprintf(stderr, "Missing plugin URI, try lv2ls to list plugins\n"); - return EXIT_FAILURE; - } - - /* Find plugin */ - printf("Plugin: %s\n", lilv_node_as_string(plugin_uri)); - jalv.plugin = lilv_plugins_get_by_uri(plugins, plugin_uri); - lilv_node_free(plugin_uri); - if (!jalv.plugin) { - fprintf(stderr, "Failed to find plugin\n"); - lilv_world_free(world); - return EXIT_FAILURE; - } - - /* Load preset, if specified */ - if (jalv.opts.preset) { - LilvNode* preset = lilv_new_uri(jalv.world, jalv.opts.preset); - - jalv_load_presets(&jalv, NULL, NULL); - state = lilv_state_new_from_world(jalv.world, &jalv.map, preset); - jalv.preset = state; - lilv_node_free(preset); - if (!state) { - fprintf(stderr, "Failed to find preset <%s>\n", jalv.opts.preset); - lilv_world_free(world); - return EXIT_FAILURE; - } - } - - /* Check that any required features are supported */ - LilvNodes* req_feats = lilv_plugin_get_required_features(jalv.plugin); - LILV_FOREACH(nodes, f, req_feats) { - const char* uri = lilv_node_as_uri(lilv_nodes_get(req_feats, f)); - if (!feature_is_supported(uri)) { - fprintf(stderr, "Feature %s is not supported\n", uri); - lilv_world_free(world); - return EXIT_FAILURE; - } - } - lilv_nodes_free(req_feats); - - /* Check for thread-safe state restore() method. */ - LilvNode* state_threadSafeRestore = lilv_new_uri( - jalv.world, LV2_STATE__threadSafeRestore); - if (lilv_plugin_has_feature(jalv.plugin, state_threadSafeRestore)) { - jalv.safe_restore = true; - } - lilv_node_free(state_threadSafeRestore); - - if (!state) { - /* Not restoring state, load the plugin as a preset to get default */ - state = lilv_state_new_from_world( - jalv.world, &jalv.map, lilv_plugin_get_uri(jalv.plugin)); - } + /* if (!plugin_uri) { */ + /* fprintf(stderr, "Missing plugin URI, try lv2ls to list plugins\n"); */ + /* return EXIT_FAILURE; */ + /* } */ - /* Get a plugin UI */ - const char* native_ui_type_uri = jalv_native_ui_type(&jalv); - jalv.uis = lilv_plugin_get_uis(jalv.plugin); - if (!jalv.opts.generic_ui && native_ui_type_uri) { - const LilvNode* native_ui_type = lilv_new_uri(jalv.world, native_ui_type_uri); - LILV_FOREACH(uis, u, jalv.uis) { - const LilvUI* this_ui = lilv_uis_get(jalv.uis, u); - if (lilv_ui_is_supported(this_ui, - suil_ui_supported, - native_ui_type, - &jalv.ui_type)) { - /* TODO: Multiple UI support */ - jalv.ui = this_ui; - break; - } + if (plugin_uri) { + /* Load plugin */ + const int st = jalv_load_plugin(&jalv, plugin_uri, state); + if (st) { + return st; } - } else if (!jalv.opts.generic_ui && jalv.opts.show_ui) { - jalv.ui = lilv_uis_get(jalv.uis, lilv_uis_begin(jalv.uis)); } - /* Create ringbuffers for UI if necessary */ - if (jalv.ui) { - fprintf(stderr, "UI: %s\n", - lilv_node_as_uri(lilv_ui_get_uri(jalv.ui))); - } else { - fprintf(stderr, "UI: None\n"); - } - - /* Create port and control structures */ - jalv_create_ports(&jalv); - jalv_create_controls(&jalv, true); - jalv_create_controls(&jalv, false); - - if (!(jalv.backend = jalv_backend_init(&jalv))) { - die("Failed to connect to audio system"); - } - - printf("Sample rate: %u Hz\n", jalv.sample_rate); - printf("Block length: %u frames\n", jalv.block_length); - printf("MIDI buffers: %zu bytes\n", jalv.midi_buf_size); - - if (jalv.opts.buffer_size == 0) { - /* The UI ring is fed by plugin output ports (usually one), and the UI - updates roughly once per cycle. The ring size is a few times the - size of the MIDI output to give the UI a chance to keep up. The UI - should be able to keep up with 4 cycles, and tests show this works - for me, but this value might need increasing to avoid overflows. - */ - jalv.opts.buffer_size = jalv.midi_buf_size * N_BUFFER_CYCLES; - } - - if (jalv.opts.update_rate == 0.0) { - /* Calculate a reasonable UI update frequency. */ - jalv.ui_update_hz = (float)jalv.sample_rate / jalv.midi_buf_size * 2.0f; - jalv.ui_update_hz = MAX(25.0f, jalv.ui_update_hz); - } else { - /* Use user-specified UI update rate. */ - jalv.ui_update_hz = jalv.opts.update_rate; - jalv.ui_update_hz = MAX(1.0f, jalv.ui_update_hz); - } - - /* The UI can only go so fast, clamp to reasonable limits */ - jalv.ui_update_hz = MIN(60, jalv.ui_update_hz); - jalv.opts.buffer_size = MAX(4096, jalv.opts.buffer_size); - fprintf(stderr, "Comm buffers: %d bytes\n", jalv.opts.buffer_size); - fprintf(stderr, "Update rate: %.01f Hz\n", jalv.ui_update_hz); - - /* Build options array to pass to plugin */ - const LV2_Options_Option options[] = { - { LV2_OPTIONS_INSTANCE, 0, jalv.urids.param_sampleRate, - sizeof(float), jalv.urids.atom_Float, &jalv.sample_rate }, - { LV2_OPTIONS_INSTANCE, 0, jalv.urids.bufsz_minBlockLength, - sizeof(int32_t), jalv.urids.atom_Int, &jalv.block_length }, - { LV2_OPTIONS_INSTANCE, 0, jalv.urids.bufsz_maxBlockLength, - sizeof(int32_t), jalv.urids.atom_Int, &jalv.block_length }, - { LV2_OPTIONS_INSTANCE, 0, jalv.urids.bufsz_sequenceSize, - sizeof(int32_t), jalv.urids.atom_Int, &jalv.midi_buf_size }, - { LV2_OPTIONS_INSTANCE, 0, jalv.urids.ui_updateRate, - sizeof(float), jalv.urids.atom_Float, &jalv.ui_update_hz }, - { LV2_OPTIONS_INSTANCE, 0, 0, 0, 0, NULL } - }; - - options_feature.data = (void*)&options; - - /* Create Plugin <=> UI communication buffers */ - jalv.ui_events = zix_ring_new(jalv.opts.buffer_size); - jalv.plugin_events = zix_ring_new(jalv.opts.buffer_size); - zix_ring_mlock(jalv.ui_events); - zix_ring_mlock(jalv.plugin_events); - - /* Instantiate the plugin */ - jalv.instance = lilv_plugin_instantiate( - jalv.plugin, jalv.sample_rate, features); - if (!jalv.instance) { - die("Failed to instantiate plugin.\n"); - } - - ext_data.data_access = lilv_instance_get_descriptor(jalv.instance)->extension_data; - - fprintf(stderr, "\n"); - if (!jalv.buf_size_set) { - jalv_allocate_port_buffers(&jalv); - } - - /* Create workers if necessary */ - if (lilv_plugin_has_feature(jalv.plugin, jalv.nodes.work_schedule) - && lilv_plugin_has_extension_data(jalv.plugin, jalv.nodes.work_interface)) { - const LV2_Worker_Interface* iface = (const LV2_Worker_Interface*) - lilv_instance_get_extension_data(jalv.instance, LV2_WORKER__interface); - - jalv_worker_init(&jalv, &jalv.worker, iface, true); - if (jalv.safe_restore) { - jalv_worker_init(&jalv, &jalv.state_worker, iface, false); - } - } - - /* Apply loaded state to plugin instance if necessary */ - if (state) { - jalv_apply_state(&jalv, state); - } - - if (jalv.opts.controls) { - for (char** c = jalv.opts.controls; *c; ++c) { - jalv_apply_control_arg(&jalv, *c); - } - } - - /* Set Jack callbacks */ - jalv_backend_init(&jalv); - - /* Create Jack ports and connect plugin ports to buffers */ - for (uint32_t i = 0; i < jalv.num_ports; ++i) { - jalv_backend_activate_port(&jalv, i); - } - - /* Print initial control values */ - for (size_t i = 0; i < jalv.controls.n_controls; ++i) { - ControlID* control = jalv.controls.controls[i]; - if (control->type == PORT) {// && control->value_type == jalv->forge.Float) { - struct Port* port = &jalv.ports[control->index]; - print_control_value(&jalv, port, port->control); - } - } - - /* Activate plugin */ - lilv_instance_activate(jalv.instance); - /* Discover UI */ jalv.has_ui = jalv_discover_ui(&jalv); - /* Activate Jack */ - jalv_backend_activate(&jalv); - jalv.play_state = JALV_RUNNING; - /* Run UI (or prompt at console) */ jalv_open_ui(&jalv); |